diff options
20 files changed, 1444 insertions, 261 deletions
diff --git a/res/drawable/progress_stop.png b/res/drawable/progress_stop.png Binary files differnew file mode 100644 index 0000000..a85f987 --- /dev/null +++ b/res/drawable/progress_stop.png diff --git a/res/drawable/tab_selected_bg.9.png b/res/drawable/tab_selected_bg.9.png Binary files differnew file mode 100644 index 0000000..5e6b1ed --- /dev/null +++ b/res/drawable/tab_selected_bg.9.png diff --git a/res/drawable/tab_unselected.xml b/res/drawable/tab_unselected.xml new file mode 100644 index 0000000..1f22ae1 --- /dev/null +++ b/res/drawable/tab_unselected.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="#00000000"/> + <stroke android:width="3dp" android:color="#ff404040"/> + <padding android:left="9dp" android:top="9dp" + android:right="9dp" android:bottom="9dp" /> +</shape> diff --git a/res/drawable/tab_unselected_bg.9.png b/res/drawable/tab_unselected_bg.9.png Binary files differnew file mode 100644 index 0000000..c19443a --- /dev/null +++ b/res/drawable/tab_unselected_bg.9.png diff --git a/res/drawable/textfield_nostroke.xml b/res/drawable/textfield_nostroke.xml new file mode 100644 index 0000000..2945056 --- /dev/null +++ b/res/drawable/textfield_nostroke.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="#ffd0d0d0"/> + <stroke android:width="1dp" android:color="#ff94b73f"/> + <padding android:left="9dp" android:top="9dp" + android:right="9dp" android:bottom="9dp" /> +</shape> diff --git a/res/layout-land/title_bar_tabbed.xml b/res/layout-land/title_bar_tabbed.xml new file mode 100644 index 0000000..853dbeb --- /dev/null +++ b/res/layout-land/title_bar_tabbed.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="utf-8"?> + <!-- + Copyright 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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/tabbedtitleland" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:paddingLeft="6dip" + android:paddingRight="6dip" + android:background="#ffdddddd"> + <ImageButton + android:id="@+id/back" + android:src="@drawable/ic_arrow_left" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginRight="6dip" + android:background="@drawable/browserbarbutton" /> + <ImageButton + android:id="@+id/forward" + android:src="@drawable/ic_arrow_right" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginRight="6dip" + android:background="@drawable/browserbarbutton" /> + <ImageButton + android:id="@+id/star" + android:src="@drawable/ic_star" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginRight="6dip" + android:background="@drawable/browserbarbutton" /> + <com.android.browser.TabScrollView + android:id="@+id/tabs" + android:layout_width="0dip" + android:layout_weight="1.0" + android:layout_height="wrap_content" + android:orientation="horizontal" /> + <com.android.browser.UrlInputView + android:id="@+id/editurl" + android:layout_width="0dip" + android:layout_weight="1.0" + android:layout_height="wrap_content" + android:layout_marginLeft="3dip" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="@color/black" + android:gravity="center_vertical" + android:singleLine="true" + android:ellipsize="end" + android:lines="1" + android:scrollHorizontally="true" + android:visibility="gone" + android:background="@drawable/textfield_stroke" + android:inputType="textUri" + android:imeOptions="actionGo" /> + <ImageButton + android:id="@+id/newtab" + android:src="@drawable/ic_menu_new_window" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginRight="6dip" + android:background="@drawable/browserbarbutton" /> + <ImageButton + android:id="@+id/menu" + android:src="@drawable/ic_menu" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginRight="6dip" + android:background="@drawable/browserbarbutton" /> + <ImageButton + android:id="@+id/all_btn" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:scaleType="center" + android:background="@drawable/browserbarbutton" + android:src="@drawable/ic_pages" /> +</LinearLayout> diff --git a/res/layout/simple_dropdown_item_2line.xml b/res/layout/simple_dropdown_item_2line.xml new file mode 100644 index 0000000..8b955ec --- /dev/null +++ b/res/layout/simple_dropdown_item_2line.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/layout/simple_spinner_item.xml +** +** Copyright 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. +*/ +--> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="?android:attr/listPreferredItemHeight" + android:orientation="horizontal" + android:gravity="center_vertical" + android:baselineAligned="false"> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/icon1" + android:scaleType="center" + android:paddingLeft="2dip" + android:paddingRight="2dip" /> + <TwoLineListItem + android:paddingTop="2dip" + android:paddingBottom="2dip" + android:layout_width="0dip" + android:layout_weight="1" + android:layout_height="wrap_content" + android:mode="twoLine"> + <TextView + android:id="@android:id/text1" + style="?android:attr/dropDownItemStyle" + android:textAppearance="?android:attr/textAppearanceLargeInverse" + android:singleLine="true" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + <TextView + android:id="@android:id/text2" + style="?android:attr/dropDownItemStyle" + android:textAppearance="?android:attr/textAppearanceSmallInverse" + android:textColor="#323232" + android:singleLine="true" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@android:id/text1" + android:layout_alignLeft="@android:id/text1" /> + </TwoLineListItem> +</LinearLayout> diff --git a/res/layout/tab_title.xml b/res/layout/tab_title.xml new file mode 100644 index 0000000..28b553d --- /dev/null +++ b/res/layout/tab_title.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?> + <!-- + Copyright 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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="0dip" + android:layout_weight="1.0" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="horizontal"> + <ImageView + android:id="@+id/favicon" + android:layout_width="20dip" + android:layout_height="20dip" + android:layout_marginLeft="3dip" /> + <ImageView + android:id="@+id/lock" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="6dip" + android:visibility="gone" /> + <TextView + android:id="@+id/title" + android:layout_height="wrap_content" + android:layout_width="0dip" + android:layout_weight="1.0" + android:layout_marginLeft="3dip" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="@color/black" + android:gravity="center_vertical" + android:singleLine="true" + android:ellipsize="end" /> + <com.android.browser.CircularProgressView + android:id="@+id/stop" + android:layout_width="36dip" + android:layout_height="36dip" + android:background="@null" + android:src="@drawable/progress_stop" /> + <ImageView + android:id="@+id/close" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="6dip" + android:src="@drawable/btn_close_window" /> +</LinearLayout> diff --git a/res/layout/title_bar_tabbed.xml b/res/layout/title_bar_tabbed.xml new file mode 100644 index 0000000..fc786e0 --- /dev/null +++ b/res/layout/title_bar_tabbed.xml @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="utf-8"?> + <!-- + Copyright 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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/tabbedtitleport" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:background="#ffdddddd"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <com.android.browser.TabScrollView + android:id="@+id/tabs" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1.0" + android:orientation="horizontal" /> + <ImageButton + android:id="@+id/newtab" + android:src="@drawable/ic_menu_new_window" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:background="@drawable/browserbarbutton" /> + </LinearLayout> + <LinearLayout + android:id="@+id/urlbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:paddingLeft="6dip" + android:paddingRight="6dip"> + <ImageButton + android:id="@+id/back" + android:src="@drawable/ic_arrow_left" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginRight="6dip" + android:background="@drawable/browserbarbutton" /> + <ImageButton + android:id="@+id/forward" + android:src="@drawable/ic_arrow_right" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginRight="6dip" + android:background="@drawable/browserbarbutton" /> + <ImageButton + android:id="@+id/star" + android:src="@drawable/ic_star" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginRight="6dip" + android:background="@drawable/browserbarbutton" /> + <com.android.browser.UrlInputView + android:id="@+id/editurl" + android:layout_width="0dip" + android:layout_weight="1.0" + android:layout_height="wrap_content" + android:layout_marginLeft="3dip" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="@color/black" + android:gravity="center_vertical" + android:singleLine="true" + android:ellipsize="end" + android:lines="1" + android:scrollHorizontally="true" + android:visibility="gone" + android:background="@drawable/textfield_nostroke" + android:inputType="textUri" + android:imeOptions="actionGo" /> + <ImageButton + android:id="@+id/menu" + android:src="@drawable/ic_menu" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginRight="6dip" + android:background="@drawable/browserbarbutton" /> + <ImageButton + android:id="@+id/all_btn" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:scaleType="center" + android:background="@drawable/browserbarbutton" + android:src="@drawable/ic_pages" /> + </LinearLayout> +</LinearLayout> diff --git a/res/layout/title_bar_xlarge.xml b/res/layout/title_bar_xlarge.xml deleted file mode 100644 index 3070b8b..0000000 --- a/res/layout/title_bar_xlarge.xml +++ /dev/null @@ -1,118 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright 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. ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:background="#ffdddddd" > - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:paddingLeft="6dip" - android:paddingRight="6dip" - > - <ImageButton android:id="@+id/back" - android:src="@drawable/ic_arrow_left" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_marginRight="6dip" - android:background="@drawable/browserbarbutton" - /> - <ImageButton android:id="@+id/forward" - android:src="@drawable/ic_arrow_right" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_marginRight="6dip" - android:background="@drawable/browserbarbutton" - /> - <ImageButton android:id="@+id/star" - android:src="@drawable/ic_star" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_marginRight="6dip" - android:background="@drawable/browserbarbutton" - /> - - <LinearLayout android:id="@+id/title_bg" - android:background="@drawable/textfield_stroke" - android:layout_width="0dip" - android:layout_weight="1.0" - android:layout_height="wrap_content" - android:layout_marginRight="6dip" - android:gravity="center_vertical" - android:orientation="horizontal" - > - <ImageView android:id="@+id/favicon" - android:layout_width="20dip" - android:layout_height="20dip" - android:layout_marginLeft="3dip" - /> - <ImageView android:id="@+id/lock" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginLeft="6dip" - android:visibility="gone" - /> - <TextView - android:id="@+id/title" - android:layout_height="wrap_content" - android:layout_width="0dip" - android:layout_weight="1.0" - android:layout_marginLeft="3dip" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textColor="@color/black" - android:gravity="center_vertical" - android:singleLine="true" - android:ellipsize="end" - /> - </LinearLayout> - <ImageButton android:id="@+id/stop" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_marginRight="6dip" - android:src="@drawable/ic_stop" - android:background="@drawable/browserbarbutton" - /> - <ImageButton android:id="@+id/menu" - android:src="@drawable/ic_menu" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_marginRight="6dip" - android:background="@drawable/browserbarbutton" - /> - <ImageButton - android:id="@+id/all_btn" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:scaleType="center" - android:background="@drawable/browserbarbutton" - android:src="@drawable/ic_pages" - /> - </LinearLayout> - - <!-- Should show translucent over the webpage --> - <ProgressBar android:id="@+id/progress_horizontal" - style="?android:attr/progressBarStyleHorizontal" - android:layout_width="match_parent" - android:layout_height="14dip" - android:max="100" - /> - -</LinearLayout> diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java index ad82fd0..8afd3b4 100644 --- a/src/com/android/browser/BrowserActivity.java +++ b/src/com/android/browser/BrowserActivity.java @@ -121,9 +121,9 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Vector; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.Vector; public class BrowserActivity extends Activity implements View.OnCreateContextMenuListener, DownloadListener { @@ -214,8 +214,12 @@ public class BrowserActivity extends Activity & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE; + // Create the tab control and our initial tab + mTabControl = new TabControl(this); + + if (mXLargeScreenSize) { - mTitleBar = new TitleBarXLarge(this); + mTitleBar = new TitleBarXLarge(this, mTabControl); LinearLayout layout = (LinearLayout) mBrowserFrameLayout. findViewById(R.id.vertical_layout); layout.addView(mTitleBar, 0, new LinearLayout.LayoutParams( @@ -230,8 +234,6 @@ public class BrowserActivity extends Activity mFakeTitleBar = new TitleBar(this); } - // Create the tab control and our initial tab - mTabControl = new TabControl(this); // Open the icon database and retain all the bookmark urls for favicons retainIconsOnStartup(); @@ -851,6 +853,12 @@ public class BrowserActivity extends Activity return true; } + private void rebuildTitleBar() { + if (mXLargeScreenSize) { + ((TitleBarXLarge) mTitleBar).rebuildLayout(); + } + } + private void showFakeTitleBar() { if (mXLargeScreenSize) return; if (mFakeTitleBar.getParent() == null && mActiveTabsPage == null @@ -1060,6 +1068,7 @@ public class BrowserActivity extends Activity showHttpAuthentication(mHttpAuthHandler, null, null, title, name, password, focusId); } + rebuildTitleBar(); } @Override diff --git a/src/com/android/browser/BrowserBookmarksPage.java b/src/com/android/browser/BrowserBookmarksPage.java index 320c438..6af14e8 100644 --- a/src/com/android/browser/BrowserBookmarksPage.java +++ b/src/com/android/browser/BrowserBookmarksPage.java @@ -23,16 +23,6 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; -import android.graphics.Rect; -import android.graphics.RectF; -import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; @@ -53,6 +43,7 @@ import android.widget.AdapterView; import android.widget.GridView; import android.widget.ListView; import android.widget.Toast; +import android.widget.AdapterView.OnItemClickListener; /*package*/ enum BookmarkViewMode { NONE, GRID, LIST } /** @@ -269,13 +260,14 @@ public class BrowserBookmarksPage extends Activity implements new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... unused) { - BrowserBookmarksAdapter adapter = new BrowserBookmarksAdapter( - BrowserBookmarksPage.this, - url, - title, - thumbnail, - createShortcut, - mostVisited); + BrowserBookmarksAdapter adapter = + new BrowserBookmarksAdapter( + BrowserBookmarksPage.this, + url, + title, + thumbnail, + createShortcut, + mostVisited); mHandler.obtainMessage(ADAPTER_CREATED, adapter).sendToTarget(); return null; } @@ -352,7 +344,7 @@ public class BrowserBookmarksPage extends Activity implements } listView.setDrawSelectorOnTop(false); listView.setVerticalScrollBarEnabled(true); - listView.setOnItemClickListener(mListener); + listView.setOnItemClickListener(mListListener); if (mMostVisited) { listView.setEmptyView(mEmptyView); } @@ -416,7 +408,29 @@ public class BrowserBookmarksPage extends Activity implements } }; - private AdapterView.OnItemClickListener mListener = new AdapterView.OnItemClickListener() { + private OnItemClickListener mListener = new OnItemClickListener() { + public void onItemClick(AdapterView parent, View v, int position, long id) { + // It is possible that the view has been canceled when we get to + // this point as back has a higher priority + if (mCanceled) { + android.util.Log.e(LOGTAG, "item clicked when dismissing"); + return; + } + if (!mCreateShortcut) { + if (0 == position && !mMostVisited) { + // XXX: Work-around for a framework issue. + mHandler.sendEmptyMessage(SAVE_CURRENT_PAGE); + } else { + loadUrl(position); + } + } else { + setResultToParent(RESULT_OK, createShortcutIntent(position)); + finish(); + } + } + }; + + private OnItemClickListener mListListener = new OnItemClickListener() { public void onItemClick(AdapterView parent, View v, int position, long id) { // It is possible that the view has been canceled when we get to // this point as back has a higher priority @@ -658,4 +672,5 @@ public class BrowserBookmarksPage extends Activity implements resultCode, data); } } + } diff --git a/src/com/android/browser/BrowserProvider.java b/src/com/android/browser/BrowserProvider.java index 1eec52b..7064180 100644 --- a/src/com/android/browser/BrowserProvider.java +++ b/src/com/android/browser/BrowserProvider.java @@ -29,8 +29,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.UriMatcher; import android.content.SharedPreferences.Editor; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; +import android.content.res.Configuration; import android.database.AbstractCursor; import android.database.ContentObserver; import android.database.Cursor; @@ -47,8 +46,6 @@ import android.speech.RecognizerResultsIntent; import android.text.TextUtils; import android.util.Log; import android.util.Patterns; -import android.util.TypedValue; - import java.io.File; import java.io.FilenameFilter; @@ -91,6 +88,17 @@ public class BrowserProvider extends ContentProvider { private static final int SUGGEST_COLUMN_QUERY_ID = 8; private static final int SUGGEST_COLUMN_INTENT_EXTRA_DATA = 9; + // how many suggestions will be shown in dropdown + // 0..SHORT: filled by browser db + private static final int MAX_SUGGEST_SHORT_SMALL = 3; + // SHORT..LONG: filled by search suggestions + private static final int MAX_SUGGEST_LONG_SMALL = 6; + + // large screen size shows more + private static final int MAX_SUGGEST_SHORT_LARGE = 6; + private static final int MAX_SUGGEST_LONG_LARGE = 9; + + // shared suggestion columns private static final String[] COLUMNS = new String[] { "_id", @@ -104,10 +112,6 @@ public class BrowserProvider extends ContentProvider { SearchManager.SUGGEST_COLUMN_QUERY, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA}; - private static final int MAX_SUGGESTION_SHORT_ENTRIES = 3; - private static final int MAX_SUGGESTION_LONG_ENTRIES = 6; - private static final String MAX_SUGGESTION_LONG_ENTRIES_STRING = - Integer.valueOf(MAX_SUGGESTION_LONG_ENTRIES).toString(); // make sure that these match the index of TABLE_NAMES private static final int URI_MATCH_BOOKMARKS = 0; @@ -167,6 +171,9 @@ public class BrowserProvider extends ContentProvider { private SearchManager mSearchManager; + private int mMaxSuggestionShortSize; + private int mMaxSuggestionLongSize; + public BrowserProvider() { } @@ -350,6 +357,20 @@ public class BrowserProvider extends ContentProvider { @Override public boolean onCreate() { final Context context = getContext(); + boolean xlargeScreenSize = (context.getResources().getConfiguration().screenLayout + & Configuration.SCREENLAYOUT_SIZE_MASK) + == Configuration.SCREENLAYOUT_SIZE_XLARGE; + boolean isPortrait = (context.getResources().getConfiguration().orientation + == Configuration.ORIENTATION_PORTRAIT); + + + if (xlargeScreenSize && isPortrait) { + mMaxSuggestionLongSize = MAX_SUGGEST_LONG_LARGE; + mMaxSuggestionShortSize = MAX_SUGGEST_SHORT_LARGE; + } else { + mMaxSuggestionLongSize = MAX_SUGGEST_LONG_SMALL; + mMaxSuggestionShortSize = MAX_SUGGEST_SHORT_SMALL; + } mOpenHelper = new DatabaseHelper(context); mBackupManager = new BackupManager(context); // we added "picasa web album" into default bookmarks for version 19. @@ -467,8 +488,8 @@ public class BrowserProvider extends ContentProvider { mSuggestCursor = sc; mHistoryCount = hc != null ? hc.getCount() : 0; mSuggestionCount = sc != null ? sc.getCount() : 0; - if (mSuggestionCount > (MAX_SUGGESTION_LONG_ENTRIES - mHistoryCount)) { - mSuggestionCount = MAX_SUGGESTION_LONG_ENTRIES - mHistoryCount; + if (mSuggestionCount > (mMaxSuggestionLongSize - mHistoryCount)) { + mSuggestionCount = mMaxSuggestionLongSize - mHistoryCount; } mString = string; mIncludeWebSearch = string.length() > 0; @@ -682,6 +703,7 @@ public class BrowserProvider extends ContentProvider { } // TODO Temporary change, finalize after jq's changes go in + @Override public void deactivate() { if (mHistoryCursor != null) { mHistoryCursor.deactivate(); @@ -692,12 +714,14 @@ public class BrowserProvider extends ContentProvider { super.deactivate(); } + @Override public boolean requery() { return (mHistoryCursor != null ? mHistoryCursor.requery() : false) | (mSuggestCursor != null ? mSuggestCursor.requery() : false); } // TODO Temporary change, finalize after jq's changes go in + @Override public void close() { super.close(); if (mHistoryCursor != null) { @@ -762,12 +786,15 @@ public class BrowserProvider extends ContentProvider { public ResultsCursor(ArrayList<String> results) { mResults = results; } + @Override public int getCount() { return mResults.size(); } + @Override public String[] getColumnNames() { return RESULTS_COLUMNS; } + @Override public String getString(int column) { switch (column) { case RESULT_ACTION_ID: @@ -789,24 +816,30 @@ public class BrowserProvider extends ContentProvider { return null; } } + @Override public short getShort(int column) { throw new UnsupportedOperationException(); } + @Override public int getInt(int column) { throw new UnsupportedOperationException(); } + @Override public long getLong(int column) { if ((mPos != -1) && column == 0) { return mPos; // use row# as the _id } throw new UnsupportedOperationException(); } + @Override public float getFloat(int column) { throw new UnsupportedOperationException(); } + @Override public double getDouble(int column) { throw new UnsupportedOperationException(); } + @Override public boolean isNull(int column) { throw new UnsupportedOperationException(); } @@ -868,7 +901,7 @@ public class BrowserProvider extends ContentProvider { Cursor c = db.query(TABLE_NAMES[URI_MATCH_BOOKMARKS], SUGGEST_PROJECTION, suggestSelection, myArgs, null, null, - ORDER_BY, MAX_SUGGESTION_LONG_ENTRIES_STRING); + ORDER_BY, Integer.toString(mMaxSuggestionLongSize)); if (match == URI_MATCH_BOOKMARKS_SUGGEST || Patterns.WEB_URL.matcher(selectionArgs[0]).matches()) { @@ -877,7 +910,7 @@ public class BrowserProvider extends ContentProvider { // get Google suggest if there is still space in the list if (myArgs != null && myArgs.length > 1 && mSearchableInfo != null - && c.getCount() < (MAX_SUGGESTION_SHORT_ENTRIES - 1)) { + && c.getCount() < (mMaxSuggestionShortSize - 1)) { Cursor sc = mSearchManager.getSuggestions(mSearchableInfo, selectionArgs[0]); return new MySuggestionCursor(c, sc, selectionArgs[0]); } @@ -1120,4 +1153,10 @@ public class BrowserProvider extends ContentProvider { } } + public static Cursor getBookmarksSuggestions(ContentResolver cr, String constraint) { + Uri uri = Uri.parse("content://browser/" + SearchManager.SUGGEST_URI_PATH_QUERY); + return cr.query(uri, SUGGEST_PROJECTION, SUGGEST_SELECTION, + new String[] { constraint }, ORDER_BY); + } + } diff --git a/src/com/android/browser/CircularProgressView.java b/src/com/android/browser/CircularProgressView.java new file mode 100644 index 0000000..48f293a --- /dev/null +++ b/src/com/android/browser/CircularProgressView.java @@ -0,0 +1,140 @@ + +/* + * 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.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.widget.ImageButton; + +/** + * + */ +public class CircularProgressView extends ImageButton { + + private static final int[] ALPHAS = { + 64, 96, 128, 160, 192, 192, 160, 128, 96, 64 + }; + + // 100 ms delay between frames, 10fps + private static int ALPHA_REFRESH_DELAY = 100; + + private int mEndAngle; + private int mProgress; + private Paint mPaint; + private int mAlpha; + private boolean mAnimated; + private RectF mRect; + private int mMaxProgress; + + /** + * @param context + * @param attrs + * @param defStyle + */ + public CircularProgressView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context); + } + + /** + * @param context + * @param attrs + */ + public CircularProgressView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + /** + * @param context + */ + public CircularProgressView(Context context) { + super(context); + init(context); + } + + private void init(Context ctx) { + mEndAngle = 0; + mProgress = 0; + mMaxProgress = 100; + mPaint = new Paint(); + mPaint.setAntiAlias(true); + mPaint.setColor(Color.BLACK); + mRect = new RectF(); + } + + void setMaxProgress(int max) { + mMaxProgress = max; + } + + private synchronized boolean isAnimated() { + return mAnimated; + } + + private synchronized void setAnimated(boolean animated) { + mAnimated = animated; + } + + void setProgress(int progress) { + mProgress = progress; + mEndAngle = 360 * progress / mMaxProgress; + invalidate(); + if (!isAnimated() && (progress > 0) && (progress < mMaxProgress)) { + setAnimated(true); + mAlpha = 0; + post(new Runnable() { + @Override + public void run() { + if (isAnimated()) { + mAlpha = (mAlpha + 1) % ALPHAS.length; + mPaint.setAlpha(ALPHAS[mAlpha]); + invalidate(); + postDelayed(this, ALPHA_REFRESH_DELAY); + } + } + }); + } else if ((progress <= 0) || (progress >= mMaxProgress)) { + setAnimated(false); + } + } + + @Override + public void onDraw(Canvas canvas) { + int w = getWidth(); + int h = getHeight(); + float cx = w * 0.5f; + float cy = h * 0.5f; + mRect.set(0, 0, w, h); + if ((mProgress > 0) && (mProgress < mMaxProgress)) { + Path p = new Path(); + p.moveTo(cx, cy); + p.lineTo(cx, 0); + p.arcTo(mRect, 270, mEndAngle); + p.lineTo(cx, cy); + int state = canvas.save(); + canvas.drawPath(p, mPaint); + canvas.restoreToCount(state); + } + super.onDraw(canvas); + } + +} diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java index 6b74a6c..1d9482b 100644 --- a/src/com/android/browser/Tab.java +++ b/src/com/android/browser/Tab.java @@ -16,21 +16,13 @@ package com.android.browser; -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Map; -import java.util.Vector; - import android.app.AlertDialog; import android.app.SearchManager; import android.content.ContentResolver; import android.content.ContentValues; import android.content.DialogInterface; -import android.content.DialogInterface.OnCancelListener; import android.content.Intent; +import android.content.DialogInterface.OnCancelListener; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; @@ -71,8 +63,17 @@ import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.browser.TabControl.TabChangeListener; import com.android.common.speech.LoggingEvents; +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Vector; + /** * Class for maintaining Tabs with a main WebView and a subwindow. */ @@ -502,6 +503,9 @@ class Tab { if (mInForeground) { mActivity.onPageStarted(view, url, favicon); } + if (getTabChangeListener() != null) { + getTabChangeListener().onPageStarted(Tab.this); + } } @Override @@ -523,6 +527,9 @@ class Tab { if (mInForeground) { mActivity.onPageFinished(view, url); } + if (getTabChangeListener() != null) { + getTabChangeListener().onPageFinished(Tab.this); + } } // return true if want to hijack the url to let another app to handle it @@ -672,6 +679,7 @@ class Tab { final ContentResolver cr = mActivity.getContentResolver(); final String newUrl = url; new AsyncTask<Void, Void, Void>() { + @Override protected Void doInBackground(Void... unused) { Browser.updateVisitedHistory(cr, newUrl, true); return null; @@ -948,6 +956,9 @@ class Tab { if (mInForeground) { mActivity.onProgressChanged(view, newProgress); } + if (getTabChangeListener() != null) { + getTabChangeListener().onProgress(Tab.this, newProgress); + } } @Override @@ -957,11 +968,16 @@ class Tab { // here, if url is null, we want to reset the title mActivity.setUrlTitle(pageUrl, title); } + TabChangeListener tcl = getTabChangeListener(); + if (tcl != null) { + tcl.onUrlAndTitle(Tab.this, pageUrl,title); + } if (pageUrl == null || pageUrl.length() >= SQLiteDatabase.SQLITE_MAX_LIKE_PATTERN_LENGTH) { return; } new AsyncTask<Void, Void, Void>() { + @Override protected Void doInBackground(Void... unused) { // See if we can find the current url in our history // database and add the new title to it. @@ -1020,6 +1036,9 @@ class Tab { if (mInForeground) { mActivity.setFavicon(icon); } + if (getTabChangeListener() != null) { + getTabChangeListener().onFavicon(Tab.this, icon); + } } @Override @@ -1208,10 +1227,12 @@ class Tab { @Override public void getVisitedHistory(final ValueCallback<String[]> callback) { AsyncTask<Void, Void, String[]> task = new AsyncTask<Void, Void, String[]>() { + @Override public String[] doInBackground(Void... unused) { return Browser.getVisitedHistory(mActivity .getContentResolver()); } + @Override public void onPostExecute(String[] result) { callback.onReceiveValue(result); }; @@ -1990,4 +2011,13 @@ class Tab { LinearLayout parent = (LinearLayout) dialog.getParent(); if (parent != null) parent.removeView(dialog); } + + /** + * always get the TabChangeListener form the tab control + * @return the TabControl change listener + */ + private TabChangeListener getTabChangeListener() { + return mActivity.getTabControl().getTabChangeListener(); + } + } diff --git a/src/com/android/browser/TabControl.java b/src/com/android/browser/TabControl.java index afd4ea8..0fbdd7a 100644 --- a/src/com/android/browser/TabControl.java +++ b/src/com/android/browser/TabControl.java @@ -132,7 +132,7 @@ class TabControl { int getCurrentIndex() { return mCurrentTab; } - + /** * Given a Tab, find it's index * @param Tab to find @@ -167,6 +167,9 @@ class TabControl { mTabs.add(t); // Initially put the tab in the background. t.putInBackground(); + if (mTabChangeListener != null) { + mTabChangeListener.onNewTab(t); + } return t; } @@ -231,6 +234,9 @@ class TabControl { // Remove it from the queue of viewed tabs. mTabQueue.remove(t); + if (mTabChangeListener != null) { + mTabChangeListener.onRemoveTab(t); + } return true; } @@ -617,6 +623,47 @@ class TabControl { mainView.loadUrl(BrowserSettings.getInstance().getHomePage()); } } + if (mTabChangeListener != null) { + mTabChangeListener.onCurrentTab(newTab); + } return true; } + + interface TabChangeListener { + + public void onNewTab(Tab tab); + + public void onRemoveTab(Tab tab); + + public void onCurrentTab(Tab tab); + + public void onProgress(Tab tab, int progress); + + public void onUrlAndTitle(Tab tab, String url, String title); + + public void onFavicon(Tab tab, Bitmap favicon); + + public void onPageStarted(Tab tab); + + public void onPageFinished(Tab tab); + + } + + private TabChangeListener mTabChangeListener; + + /** + * register the TabChangeListener with the tab control + * @param listener + */ + void setOnTabChangeListener(TabChangeListener listener) { + mTabChangeListener = listener; + } + + /** + * get the current TabChangeListener (used by the tabs) + */ + TabChangeListener getTabChangeListener() { + return mTabChangeListener; + } + } diff --git a/src/com/android/browser/TabScrollView.java b/src/com/android/browser/TabScrollView.java new file mode 100644 index 0000000..6d8b91b --- /dev/null +++ b/src/com/android/browser/TabScrollView.java @@ -0,0 +1,136 @@ +/* + * 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.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.HorizontalScrollView; +import android.widget.LinearLayout; + +/** + * custom view for displaying tabs in the tabbed title bar + */ +public class TabScrollView extends HorizontalScrollView { + + private Context mContext; + + private LinearLayout mContentView; + + private int mSelected; + + /** + * @param context + * @param attrs + * @param defStyle + */ + public TabScrollView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context); + } + + /** + * @param context + * @param attrs + */ + public TabScrollView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + /** + * @param context + */ + public TabScrollView(Context context) { + super(context); + init(context); + } + + private void init(Context ctx) { + mContext = ctx; + setHorizontalScrollBarEnabled(false); + mContentView = new LinearLayout(mContext); + mContentView.setOrientation(LinearLayout.HORIZONTAL); + mContentView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT)); + addView(mContentView); + mSelected = -1; + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + ensureChildVisible(getSelectedTab()); + } + + void setSelectedTab(int position) { + View v = getSelectedTab(); + if (v != null) { + v.setSelected(false); + } + mSelected = position; + v = getSelectedTab(); + if (v != null) { + v.setSelected(true); + } + requestLayout(); + } + + View getSelectedTab() { + if ((mSelected >= 0) && (mSelected < mContentView.getChildCount())) { + return mContentView.getChildAt(mSelected); + } else { + return null; + } + } + + void clearTabs() { + mContentView.removeAllViews(); + } + + void addTab(View tab) { + mContentView.addView(tab); + tab.setSelected(false); + } + + void addTab(View tab, int pos) { + mContentView.addView(tab, pos); + tab.setSelected(false); + } + + void removeTab(View tab) { + mContentView.removeView(tab); + } + + void ensureChildVisible(View child) { + if (child != null) { + int childl = child.getLeft(); + int childr = childl + child.getWidth(); + int viewl = getScrollX(); + int viewr = viewl + getWidth(); + if (childl < viewl) { + // need scrolling to left + scrollTo(childl, 0); + } else if (childr > viewr) { + // need scrolling to right + scrollTo(childr - viewr + viewl, 0); + } + } + } + + +} diff --git a/src/com/android/browser/TitleBarBase.java b/src/com/android/browser/TitleBarBase.java index 3d234e8..7016dc0 100644 --- a/src/com/android/browser/TitleBarBase.java +++ b/src/com/android/browser/TitleBarBase.java @@ -35,7 +35,7 @@ public class TitleBarBase extends LinearLayout { protected ImageView mFavicon; protected ImageView mLockIcon; - private Drawable mGenericFavicon; + protected Drawable mGenericFavicon; public TitleBarBase(Context context) { super(context, null); diff --git a/src/com/android/browser/TitleBarXLarge.java b/src/com/android/browser/TitleBarXLarge.java index 0d799a8..fd6d67b 100644 --- a/src/com/android/browser/TitleBarXLarge.java +++ b/src/com/android/browser/TitleBarXLarge.java @@ -16,108 +16,168 @@ package com.android.browser; +import android.app.SearchManager; import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.drawable.Animatable; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import android.util.DisplayMetrics; -import android.util.TypedValue; +import android.graphics.drawable.LayerDrawable; +import android.graphics.drawable.PaintDrawable; import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.MenuInflater; import android.view.View; import android.widget.ImageView; -import android.widget.ProgressBar; +import android.widget.LinearLayout; import android.widget.TextView; -import com.android.common.speech.LoggingEvents; +import com.android.browser.TabControl.TabChangeListener; +import com.android.browser.UrlInputView.UrlInputListener; + +import java.util.HashMap; +import java.util.Map; /** - * This class represents a title bar for a particular "tab" or "window" in the - * browser. + * tabbed title bar for xlarge screen browser */ -public class TitleBarXLarge extends TitleBarBase { - private Drawable mCircularProgress; - private ProgressBar mHorizontalProgress; - private Drawable mStopDrawable; - private Drawable mReloadDrawable; - private boolean mInLoad; - private BrowserActivity mBrowserActivity; - - private final View mBackButton; - private final View mForwardButton; - private final View mStar; - private final View mMenu; - private final ImageView mStopButton; - private final TextView mTitle; - private final View mAllButton; - - public TitleBarXLarge(BrowserActivity context) { +public class TitleBarXLarge extends TitleBarBase + implements TabChangeListener, UrlInputListener { + + private static final int PROGRESS_MAX = 100; + + private static final int TAB_WIDTH_SELECTED = 400; + private static final int TAB_WIDTH_UNSELECTED = 150; + + private BrowserActivity mBrowserActivity; + private Drawable mStopDrawable; + private Drawable mReloadDrawable; + private Drawable mSelectedBackground; + private Drawable mUnselectedBackground; + + private View mBackButton; + private View mForwardButton; + private View mStar; + private View mMenu; + private View mAllButton; + private TabScrollView mTabs; + private View mNewButton; + private TabControl mControl; + private UrlInputView mUrlView; + + private boolean mIsInLandscape; + private Map<Tab, TabViewData> mTabMap; + + private float mDensityScale; + + public TitleBarXLarge(BrowserActivity context, TabControl tabcontrol) { super(context); - Resources resources = context.getResources(); - LayoutInflater factory = LayoutInflater.from(context); - factory.inflate(R.layout.title_bar_xlarge, this); + mDensityScale = context.getResources().getDisplayMetrics().density; + mTabMap = new HashMap<Tab, TabViewData>(); mBrowserActivity = context; + mControl = tabcontrol; + Resources resources = context.getResources(); + mSelectedBackground = resources.getDrawable(R.drawable.tab_selected_bg); + mUnselectedBackground = resources.getDrawable(R.drawable.tab_unselected_bg); + mStopDrawable = resources.getDrawable(R.drawable.progress_stop); + mReloadDrawable = resources.getDrawable(R.drawable.ic_reload); + rebuildLayout(context, true); + // register the tab change listener + mControl.setOnTabChangeListener(this); + } - mTitle = (TextView) findViewById(R.id.title); - mTitle.setCompoundDrawablePadding(5); - mTitle.setLongClickable(true); + void rebuildLayout() { + rebuildLayout(mBrowserActivity, false); + } - mLockIcon = (ImageView) findViewById(R.id.lock); - mFavicon = (ImageView) findViewById(R.id.favicon); - mStopButton = (ImageView) findViewById(R.id.stop); - mStopDrawable = mStopButton.getDrawable(); - mReloadDrawable = resources.getDrawable(R.drawable.ic_reload); + private void rebuildLayout(Context context, boolean rebuildData) { + removeAllViews(); + LayoutInflater factory = LayoutInflater.from(context); + factory.inflate(R.layout.title_bar_tabbed, this); - mAllButton = (ImageView) findViewById(R.id.all_btn); - mCircularProgress = (Drawable) resources.getDrawable( - com.android.internal.R.drawable.search_spinner); - DisplayMetrics metrics = resources.getDisplayMetrics(); - int iconDimension = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, 20f, metrics); - mCircularProgress.setBounds(0, 0, iconDimension, iconDimension); - mHorizontalProgress = (ProgressBar) findViewById( - R.id.progress_horizontal); - mHorizontalProgress.setProgressDrawable( - resources.getDrawable(R.drawable.progress)); - - // FIXME: Change enabled states based on whether you can go + mTabs = (TabScrollView) findViewById(R.id.tabs); + mNewButton = findViewById(R.id.newtab); + mUrlView = (UrlInputView) findViewById(R.id.editurl); + mAllButton = findViewById(R.id.all_btn); + // TODO: Change enabled states based on whether you can go // back/forward. Probably should be done inside onPageStarted. mBackButton = findViewById(R.id.back); mForwardButton = findViewById(R.id.forward); mStar = findViewById(R.id.star); mMenu = findViewById(R.id.menu); View.OnClickListener listener = new View.OnClickListener() { - public void onClick(View v) { - if (mBackButton == v) { - mBrowserActivity.getTopWindow().goBack(); - } else if (mForwardButton == v) { - mBrowserActivity.getTopWindow().goForward(); - } else if (mStar == v) { - mBrowserActivity.promptAddOrInstallBookmark(); - } else if (mMenu == v) { - mBrowserActivity.openOptionsMenu(); - } else if (mStopButton == v) { - if (mInLoad) { - mBrowserActivity.stopLoading(); - } else { - mBrowserActivity.getTopWindow().reload(); - } - } else if (mTitle == v) { - mBrowserActivity.editUrl(); - } else if (mAllButton == v) { - // FIXME: Show the new bookmarks/windows view. - mBrowserActivity.bookmarksOrHistoryPicker(false); - } + public void onClick(View v) { + if (mBackButton == v) { + mBrowserActivity.getTopWindow().goBack(); + } else if (mForwardButton == v) { + mBrowserActivity.getTopWindow().goForward(); + } else if (mStar == v) { + mBrowserActivity.promptAddOrInstallBookmark(); + } else if (mMenu == v) { + mBrowserActivity.openOptionsMenu(); + } else if (mAllButton == v) { + // TODO: Show the new bookmarks/windows view. + mBrowserActivity.bookmarksOrHistoryPicker(false); + } else if (mNewButton == v) { + mBrowserActivity.openTabToHomePage(); } + } }; mBackButton.setOnClickListener(listener); mForwardButton.setOnClickListener(listener); mStar.setOnClickListener(listener); - mStopButton.setOnClickListener(listener); - mTitle.setOnClickListener(listener); mAllButton.setOnClickListener(listener); mMenu.setOnClickListener(listener); + mNewButton.setOnClickListener(listener); + + mIsInLandscape = mBrowserActivity.getResources().getConfiguration().orientation + == Configuration.ORIENTATION_LANDSCAPE; + mUrlView.setVisibility(mIsInLandscape ? View.GONE : View.VISIBLE); + mUrlView.setUrlInputListener(this); + buildTabs(rebuildData); + // ensure title bar state + onCurrentTab(mControl.getCurrentTab()); + } + + void showUrlEditor(TabViewData tabdata) { + mUrlView.setVisibility(View.VISIBLE); + if (mIsInLandscape) { + mTabs.setVisibility(View.GONE); + mUrlView.requestFocus(); + mUrlView.forceIme(); + } + } + + void hideUrlEditor() { + Tab tab = mControl.getCurrentTab(); + if (mIsInLandscape) { + mUrlView.setVisibility(View.GONE); + mTabs.setVisibility(View.VISIBLE); + } else { + // portrait mode + mUrlView.setText(tab.getWebView().getUrl()); + } + tab.getWebView().requestFocus(); + } + + + // UrlInputListener implementation + + @Override + public void onAction(String text) { + hideUrlEditor(); + Intent i = new Intent(); + i.setAction(Intent.ACTION_SEARCH); + i.putExtra(SearchManager.QUERY, text); + mBrowserActivity.onNewIntent(i); + } + + @Override + public void onDismiss() { + hideUrlEditor(); } @Override @@ -127,44 +187,321 @@ public class TitleBarXLarge extends TitleBarBase { mBrowserActivity.onCreateContextMenu(menu, this, null); } + @Override + /* package */ void setLock(Drawable d) { + // TODO: handle in tab specific callback + } + + @Override + /* package */ void setFavicon(Bitmap icon) { + // this is handled in the tab specific callback + } + /** * Update the progress, from 0 to 100. */ + @Override /* package */ void setProgress(int newProgress) { - if (newProgress >= mHorizontalProgress.getMax()) { - mTitle.setCompoundDrawables(null, null, null, null); - ((Animatable) mCircularProgress).stop(); - mHorizontalProgress.setVisibility(View.GONE); - mInLoad = false; - mStopButton.setImageDrawable(mReloadDrawable); + // this is handled in tab specific callback + } + + @Override + /* package */ void setDisplayTitle(String title) { + // this is done in tab specific callback + } + + private void buildTabs(boolean needsRebuilding) { + mTabs.clearTabs(); + for (int i = 0; i < mControl.getTabCount(); i++) { + Tab tab = mControl.getTab(i); + TabViewData data = buildTab(needsRebuilding, tab); + TabView tv = buildView(data); + } + mTabs.setSelectedTab(mControl.getCurrentIndex()); + } + + private TabViewData buildTab(boolean needsRebuilding, Tab tab) { + TabViewData data = null; + if (needsRebuilding) { + data = new TabViewData(tab); + mTabMap.put(tab, data); } else { - mHorizontalProgress.setProgress(newProgress); - if (!mInLoad && getWindowToken() != null) { - // checking the window token lets us be sure that we - // are attached to a window before starting the animation, - // preventing a potential race condition - // (fix for bug http://b/2115736) - mTitle.setCompoundDrawables(null, null, mCircularProgress, - null); - ((Animatable) mCircularProgress).start(); - mHorizontalProgress.setVisibility(View.VISIBLE); - mInLoad = true; - mStopButton.setImageDrawable(mStopDrawable); - } + data = mTabMap.get(tab); } + return data; + } + + private TabView buildView(final TabViewData data) { + TabView tv = new TabView(mBrowserActivity, data); + tv.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (mTabs.getSelectedTab() == v) { + showUrlEditor(data); + } else { + int ix = mControl.getTabIndex(data.mTab); + mTabs.setSelectedTab(ix); + mBrowserActivity.switchToTab(ix); + } + } + }); + mTabs.addTab(tv); + return tv; } /** - * Update the text displayed in the title bar. - * @param title String to display. If null, the loading string will be - * shown. + * the views used in the tab bar */ - /* package */ void setDisplayTitle(String title) { - if (title == null) { - mTitle.setText(R.string.title_bar_loading); - } else { + class TabView extends LinearLayout { + + TabViewData mTabData; + View mTabContent; + TextView mTitle; + ImageView mIconView; + ImageView mLock; + CircularProgressView mStop; + ImageView mClose; + boolean mSelected; + boolean mInLoad; + + /** + * @param context + */ + public TabView(Context context, TabViewData tab) { + super(context); + mTabData = tab; + LayoutInflater inflater = LayoutInflater.from(mContext); + mTabContent = inflater.inflate(R.layout.tab_title, this); + mTitle = (TextView) mTabContent.findViewById(R.id.title); + mIconView = (ImageView) mTabContent.findViewById(R.id.favicon); + mLock = (ImageView) mTabContent.findViewById(R.id.lock); + mStop = (CircularProgressView) mTabContent.findViewById(R.id.stop); + mStop.setMaxProgress(PROGRESS_MAX); + mClose = (ImageView) mTabContent.findViewById(R.id.close); + mClose.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + closeTab(); + } + }); + mStop.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (mInLoad) { + mBrowserActivity.stopLoading(); + } else { + mBrowserActivity.getTopWindow().reload(); + } + } + }); + mSelected = false; + mInLoad = false; + // update the status + updateFromData(); + } + + private void updateFromData() { + mTabData.mTabView = this; + if (mTabData.mUrl != null) { + setDisplayTitle(mTabData.mUrl); + } + if (mTabData.mTitle != null) { + setDisplayTitle(mTabData.mTitle); + } + setProgress(mTabData.mProgress); + if (mTabData.mIcon != null) { + setFavicon(mTabData.mIcon); + } + if (mTabData.mLock != null) { + setLock(mTabData.mLock); + } + } + + @Override + public void setSelected(boolean selected) { + mSelected = selected; + mStop.setVisibility(mSelected ? View.VISIBLE : View.GONE); + mIconView.setVisibility(mSelected ? View.VISIBLE : View.GONE); + super.setSelected(selected); + setBackgroundDrawable(selected ? mSelectedBackground + : mUnselectedBackground); + setLayoutParams(new LayoutParams(selected ? + (int) (TAB_WIDTH_SELECTED * mDensityScale) + : (int) (TAB_WIDTH_UNSELECTED * mDensityScale), + LayoutParams.WRAP_CONTENT)); + } + + 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); + } + } + + void setTitleCompoundDrawables(Drawable left, Drawable top, + Drawable right, Drawable bottom) { + mTitle.setCompoundDrawables(left, top, right, bottom); + } + + void setProgress(int newProgress) { + mStop.setProgress(newProgress); + if (newProgress >= PROGRESS_MAX) { + mInLoad = false; + mStop.setImageDrawable(mReloadDrawable); + } else { + if (!mInLoad && getWindowToken() != null) { + // checking the window token lets us be sure that we + // are attached to a window before starting the animation, + // preventing a potential race condition + // (fix for bug http://b/2115736) + mInLoad = true; + mStop.setImageDrawable(mStopDrawable); + } + } + } + + private void closeTab() { + if (mTabData.mTab == mControl.getCurrentTab()) { + mBrowserActivity.closeCurrentWindow(); + } else { + mBrowserActivity.closeTab(mTabData.mTab); + } + } + + } + + /** + * class to store tab state within the title bar + */ + class TabViewData { + + Tab mTab; + TabView mTabView; + int mProgress; + Drawable mIcon; + Drawable mLock; + String mTitle; + String mUrl; + + TabViewData(Tab tab) { + mTab = tab; + } + + void setUrlAndTitle(String url, String title) { + mUrl = url; + mTitle = title; + if (mTabView != null) { + if (title != null) { + mTabView.setDisplayTitle(title); + } else if (url != null) { + mTabView.setDisplayTitle(url); + } + } + } + + void setProgress(int newProgress) { + mProgress = newProgress; + if (mTabView != null) { + mTabView.setProgress(mProgress); + } + } + + void setFavicon(Bitmap icon) { + Drawable[] array = new Drawable[3]; + array[0] = new PaintDrawable(Color.BLACK); + array[1] = new PaintDrawable(Color.WHITE); + if (icon == null) { + array[2] = mGenericFavicon; + } else { + array[2] = new BitmapDrawable(icon); + } + LayerDrawable d = new LayerDrawable(array); + d.setLayerInset(1, 1, 1, 1, 1); + d.setLayerInset(2, 2, 2, 2, 2); + mIcon = d; + if (mTabView != null) { + mTabView.setFavicon(mIcon); + } + } + + } + + // TabChangeListener implementation + + @Override + public void onCurrentTab(Tab tab) { + mTabs.setSelectedTab(mControl.getCurrentIndex()); + TabViewData tvd = mTabMap.get(tab); + if (tvd != null) { + if (tvd.mUrl != null) { + mUrlView.setText(tvd.mUrl); + } + setProgress(tvd.mProgress); + } + } + + @Override + public void onFavicon(Tab tab, Bitmap favicon) { + TabViewData tvd = mTabMap.get(tab); + if (tvd != null) { + tvd.setFavicon(favicon); + } + } + + @Override + public void onNewTab(Tab tab) { + TabViewData tvd = buildTab(true, tab); + buildView(tvd); + } + + @Override + public void onProgress(Tab tab, int progress) { + TabViewData tvd = mTabMap.get(tab); + if (tvd != null) { + tvd.setProgress(progress); + } + if (tab == mControl.getCurrentTab()) { + setProgress(progress); + } + } + + @Override + public void onRemoveTab(Tab tab) { + TabViewData tvd = mTabMap.get(tab); + TabView tv = tvd.mTabView; + if (tv != null) { + mTabs.removeTab(tv); + } + mTabMap.remove(tab); + } + + @Override + public void onUrlAndTitle(Tab tab, String url, String title) { + TabViewData tvd = mTabMap.get(tab); + if (tvd != null) { + tvd.setUrlAndTitle(url, title); + } + if ((url != null) && (tab == mControl.getCurrentTab())) { + mUrlView.setText(url); + } + } + + @Override + public void onPageFinished(Tab tab) { + } + + @Override + public void onPageStarted(Tab tab) { } } diff --git a/src/com/android/browser/UrlInputView.java b/src/com/android/browser/UrlInputView.java new file mode 100644 index 0000000..3841257 --- /dev/null +++ b/src/com/android/browser/UrlInputView.java @@ -0,0 +1,206 @@ +/* + * 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.app.SearchManager; +import android.content.ContentResolver; +import android.content.Context; +import android.database.Cursor; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.AutoCompleteTextView; +import android.widget.CursorAdapter; +import android.widget.Filterable; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.AdapterView.OnItemClickListener; + +/** + * url/search input view + * handling suggestions + */ +public class UrlInputView extends AutoCompleteTextView { + + private UrlInputListener mListener; + private InputMethodManager mInputManager; + private SuggestionsAdapter mAdapter; + private Drawable mFocusDrawable; + private Drawable mNoFocusDrawable; + + + public UrlInputView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context); + } + + public UrlInputView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public UrlInputView(Context context) { + super(context); + init(context); + } + + private void init(Context ctx) { + mFocusDrawable = ctx.getResources().getDrawable(R.drawable.textfield_stroke); + mNoFocusDrawable = ctx.getResources().getDrawable(R.drawable.textfield_nostroke); + mInputManager = (InputMethodManager) ctx.getSystemService(Context.INPUT_METHOD_SERVICE); + setOnEditorActionListener(new OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + finishInput(getText().toString()); + return true; + } + }); + setOnFocusChangeListener(new OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + setBackgroundDrawable(hasFocus ? mFocusDrawable : mNoFocusDrawable); + } + }); + final ContentResolver cr = mContext.getContentResolver(); + mAdapter = new SuggestionsAdapter(mContext, + BrowserProvider.getBookmarksSuggestions(cr, null)); + setAdapter(mAdapter); + setOnItemClickListener(new OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + String url = mAdapter.getViewString(view); + finishInput(url); + } + }); + setSelectAllOnFocus(true); + } + + public void setUrlInputListener(UrlInputListener listener) { + mListener = listener; + } + + public void forceIme() { + mInputManager.showSoftInput(this, 0); + } + + private void finishInput(String url) { + this.dismissDropDown(); + mInputManager.hideSoftInputFromWindow(getWindowToken(), 0); + if (url == null) { + mListener.onDismiss(); + } else { + mListener.onAction(url); + } + + } + + @Override + public boolean onKeyPreIme(int keyCode, KeyEvent evt) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + // catch back key in order to do slightly more cleanup than usual + finishInput(null); + return true; + } + return super.onKeyPreIme(keyCode, evt); + } + + interface UrlInputListener { + + public void onDismiss(); + + public void onAction(String text); + + } + + /** + * adapter used by suggestion dropdown + */ + class SuggestionsAdapter extends CursorAdapter implements Filterable { + + private Cursor mLastCursor; + private ContentResolver mContent; + private int mIndexText1; + private int mIndexText2; + private int mIndexIcon; + + public SuggestionsAdapter(Context context, Cursor c) { + super(context, c); + mContent = context.getContentResolver(); + mIndexText1 = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1); + mIndexText2 = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2_URL); + mIndexIcon = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1); + } + + public String getViewString(View view) { + TextView tv2 = (TextView) view.findViewById(android.R.id.text2); + if (tv2.getText().length() > 0) { + return tv2.getText().toString(); + } else { + TextView tv1 = (TextView) view.findViewById(android.R.id.text1); + return tv1.getText().toString(); + } + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + final LayoutInflater inflater = LayoutInflater.from(context); + final View view = inflater.inflate( + R.layout.simple_dropdown_item_2line, parent, false); + bindView(view, context, cursor); + return view; + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + TextView tv1 = (TextView) view.findViewById(android.R.id.text1); + TextView tv2 = (TextView) view.findViewById(android.R.id.text2); + ImageView ic1 = (ImageView) view.findViewById(R.id.icon1); + tv1.setText(cursor.getString(mIndexText1)); + String url = cursor.getString(mIndexText2); + tv2.setText((url != null) ? url : ""); + // assume an id + try { + int id = Integer.parseInt(cursor.getString(mIndexIcon)); + Drawable d = context.getResources().getDrawable(id); + ic1.setImageDrawable(d); + } catch (NumberFormatException nfx) { + } + } + + @Override + public String convertToString(Cursor cursor) { + return cursor.getString(mIndexText1); + } + + @Override + public Cursor runQueryOnBackgroundThread(CharSequence constraint) { + if (getFilterQueryProvider() != null) { + return getFilterQueryProvider().runQuery(constraint); + } + mLastCursor = BrowserProvider.getBookmarksSuggestions(mContent, + (constraint != null) ? constraint.toString() : null); + return mLastCursor; + } + + } + +} |