Use list UI with corpus selector
TODO: The corpus selection window has the wrong vertical position.
TODO: Suggestion icon clicks don't work.
TODO: The corpus selector does not show focused and pressed states.
Change-Id: I950308f5f32e11f39dabedfcacdd8e78701e6a57
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6844481..3068f82 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -44,6 +44,17 @@
</intent-filter>
</activity>
+ <activity android:name=".SelectSearchSourceActivity"
+ android:theme="@style/Theme.SelectSearchSource"
+ android:noHistory="true"
+ android:windowSoftInputMode="stateUnchanged|adjustResize"
+ >
+ <intent-filter>
+ <action android:name="android.intent.action.SELECT_SEARCH_SOURCE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<activity android:name=".SearchSettings"
android:label="@string/search_settings">
<intent-filter>
diff --git a/res/drawable-hdpi/global_search_source.png b/res/drawable-hdpi/global_search_source.png
new file mode 100644
index 0000000..15c632d
--- /dev/null
+++ b/res/drawable-hdpi/global_search_source.png
Binary files differ
diff --git a/res/drawable-hdpi/promoted_tab_icon.png b/res/drawable-hdpi/promoted_tab_icon.png
deleted file mode 100644
index 4213050..0000000
--- a/res/drawable-hdpi/promoted_tab_icon.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/select_search_source_bg.9.png b/res/drawable-hdpi/select_search_source_bg.9.png
new file mode 100644
index 0000000..793f1d9
--- /dev/null
+++ b/res/drawable-hdpi/select_search_source_bg.9.png
Binary files differ
diff --git a/res/drawable-hdpi/source_selector_arrow.png b/res/drawable-hdpi/source_selector_arrow.png
new file mode 100644
index 0000000..b93a0c0
--- /dev/null
+++ b/res/drawable-hdpi/source_selector_arrow.png
Binary files differ
diff --git a/res/drawable-hdpi/tab_focus.9.png b/res/drawable-hdpi/tab_focus.9.png
deleted file mode 100755
index 234e85e..0000000
--- a/res/drawable-hdpi/tab_focus.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/tab_press.9.png b/res/drawable-hdpi/tab_press.9.png
deleted file mode 100755
index 7d31ec4..0000000
--- a/res/drawable-hdpi/tab_press.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/tab_selected.9.png b/res/drawable-hdpi/tab_selected.9.png
deleted file mode 100644
index 581ff08..0000000
--- a/res/drawable-hdpi/tab_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/tab_strip_bg_focused.9.png b/res/drawable-hdpi/tab_strip_bg_focused.9.png
deleted file mode 100644
index 3fdcdb9..0000000
--- a/res/drawable-hdpi/tab_strip_bg_focused.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/tab_strip_bg_pressed.9.png b/res/drawable-hdpi/tab_strip_bg_pressed.9.png
deleted file mode 100644
index 7d26f39..0000000
--- a/res/drawable-hdpi/tab_strip_bg_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/tab_strip_bg_unfocused.9.png b/res/drawable-hdpi/tab_strip_bg_unfocused.9.png
deleted file mode 100644
index d0e5aa2..0000000
--- a/res/drawable-hdpi/tab_strip_bg_unfocused.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/tab_unselected.9.png b/res/drawable-hdpi/tab_unselected.9.png
deleted file mode 100644
index 26a34a8..0000000
--- a/res/drawable-hdpi/tab_unselected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/global_search_source.png b/res/drawable-mdpi/global_search_source.png
new file mode 100644
index 0000000..bbe742c
--- /dev/null
+++ b/res/drawable-mdpi/global_search_source.png
Binary files differ
diff --git a/res/drawable-mdpi/promoted_tab_icon.png b/res/drawable-mdpi/promoted_tab_icon.png
deleted file mode 100755
index a9bdb05..0000000
--- a/res/drawable-mdpi/promoted_tab_icon.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/select_search_source_bg.9.png b/res/drawable-mdpi/select_search_source_bg.9.png
new file mode 100644
index 0000000..777bed8
--- /dev/null
+++ b/res/drawable-mdpi/select_search_source_bg.9.png
Binary files differ
diff --git a/res/drawable-mdpi/source_selector_arrow.png b/res/drawable-mdpi/source_selector_arrow.png
new file mode 100644
index 0000000..26bf18a
--- /dev/null
+++ b/res/drawable-mdpi/source_selector_arrow.png
Binary files differ
diff --git a/res/drawable-mdpi/tab_focus.9.png b/res/drawable-mdpi/tab_focus.9.png
deleted file mode 100755
index 234e85e..0000000
--- a/res/drawable-mdpi/tab_focus.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/tab_press.9.png b/res/drawable-mdpi/tab_press.9.png
deleted file mode 100755
index 7d31ec4..0000000
--- a/res/drawable-mdpi/tab_press.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/tab_selected.9.png b/res/drawable-mdpi/tab_selected.9.png
deleted file mode 100644
index 581ff08..0000000
--- a/res/drawable-mdpi/tab_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/tab_strip_bg_focused.9.png b/res/drawable-mdpi/tab_strip_bg_focused.9.png
deleted file mode 100644
index 3fdcdb9..0000000
--- a/res/drawable-mdpi/tab_strip_bg_focused.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/tab_strip_bg_pressed.9.png b/res/drawable-mdpi/tab_strip_bg_pressed.9.png
deleted file mode 100644
index 7d26f39..0000000
--- a/res/drawable-mdpi/tab_strip_bg_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/tab_strip_bg_unfocused.9.png b/res/drawable-mdpi/tab_strip_bg_unfocused.9.png
deleted file mode 100644
index d0e5aa2..0000000
--- a/res/drawable-mdpi/tab_strip_bg_unfocused.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/tab_unselected.9.png b/res/drawable-mdpi/tab_unselected.9.png
deleted file mode 100644
index 26a34a8..0000000
--- a/res/drawable-mdpi/tab_unselected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/layout/suggestion_list.xml b/res/drawable/source_selector_bg.xml
similarity index 69%
copy from res/layout/suggestion_list.xml
copy to res/drawable/source_selector_bg.xml
index 3d677e1..fcacd89 100644
--- a/res/layout/suggestion_list.xml
+++ b/res/drawable/source_selector_bg.xml
@@ -4,9 +4,9 @@
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.
@@ -14,12 +14,8 @@
limitations under the License.
-->
-<view
- xmlns:android="http://schemas.android.com/apk/res/android"
- class="com.android.quicksearchbox.ui.SuggestionListView"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:focusable="true">
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
-</view>
+ <!-- TODO: Need focused and pressed backgrounds -->
+
+</selector>
diff --git a/res/drawable/tab_indicator_bg.xml b/res/drawable/tab_indicator_bg.xml
deleted file mode 100644
index e75c2b1..0000000
--- a/res/drawable/tab_indicator_bg.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- Non focused states -->
- <item android:state_focused="false"
- android:state_selected="false"
- android:state_pressed="false"
- android:drawable="@drawable/tab_unselected" />
- <item android:state_focused="false"
- android:state_selected="true"
- android:state_pressed="false"
- android:drawable="@drawable/tab_selected" />
-
- <!-- Focused states -->
- <item android:state_focused="true"
- android:state_selected="false"
- android:state_pressed="false"
- android:drawable="@drawable/tab_focus" />
- <item android:state_focused="true"
- android:state_selected="true"
- android:state_pressed="false"
- android:drawable="@drawable/tab_focus" />
-
- <!-- Pressed -->
- <item android:state_pressed="true" android:drawable="@drawable/tab_press" />
-</selector>
diff --git a/res/drawable/tab_strip_bg.xml b/res/drawable/tab_strip_bg.xml
deleted file mode 100644
index caa4705..0000000
--- a/res/drawable/tab_strip_bg.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item android:state_focused="false"
- android:state_pressed="false"
- android:drawable="@drawable/tab_strip_bg_unfocused" />
-
- <item android:state_focused="true"
- android:state_pressed="false"
- android:drawable="@drawable/tab_strip_bg_focused" />
-
- <item android:state_pressed="true"
- android:drawable="@drawable/tab_strip_bg_pressed" />
-
-</selector>
diff --git a/res/layout/search_bar.xml b/res/layout/search_bar.xml
index bd155de..7192db7 100644
--- a/res/layout/search_bar.xml
+++ b/res/layout/search_bar.xml
@@ -21,43 +21,16 @@
android:layout_height="fill_parent"
android:orientation="vertical">
- <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/suggestions_scroll"
+ <view
+ class="com.android.quicksearchbox.ui.SuggestionsView"
+ android:id="@+id/suggestions"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
- android:layout_below="@+id/search_edit_frame"
- >
-
- <!-- The minHeight is a trick to always be able to scroll past the
- tab strip. -->
- <view xmlns:android="http://schemas.android.com/apk/res/android"
- class="com.android.quicksearchbox.ui.SuggestionsView"
- android:id="@+id/suggestions"
- android:minHeight="400dip"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
- <TabWidget
- android:id="@android:id/tabs"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:addStatesFromChildren="true"
- android:background="@drawable/tab_strip_bg" />
- <FrameLayout
- android:id="@android:id/tabcontent"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
- </FrameLayout>
- </LinearLayout>
- </view>
-
- </ScrollView>
+ android:layout_below="@+id/search_edit_frame">
+ </view>
<!-- The search plate is after the suggestions, to give it a higher
z-index. -->
@@ -73,12 +46,15 @@
android:layout_alignParentRight="true"
>
+ <include layout="@layout/source_selector" />
+
<EditText
android:id="@+id/search_src_text"
android:background="@drawable/textfield_search"
android:layout_height="wrap_content"
android:layout_width="0dip"
android:layout_weight="1.0"
+ android:layout_marginLeft="4dip"
android:drawablePadding="2dip"
android:singleLine="true"
android:ellipsize="end"
diff --git a/res/layout/suggestion_list.xml b/res/layout/select_search_source.xml
similarity index 75%
rename from res/layout/suggestion_list.xml
rename to res/layout/select_search_source.xml
index 3d677e1..2116bb0 100644
--- a/res/layout/suggestion_list.xml
+++ b/res/layout/select_search_source.xml
@@ -4,9 +4,9 @@
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.
@@ -14,12 +14,13 @@
limitations under the License.
-->
-<view
+<GridView
xmlns:android="http://schemas.android.com/apk/res/android"
- class="com.android.quicksearchbox.ui.SuggestionListView"
- android:layout_width="fill_parent"
+ android:id="@+id/source_list"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:focusable="true">
-
-</view>
+ android:background="@drawable/select_search_source_bg"
+ android:numColumns="4"
+ android:horizontalSpacing="8dip"
+ android:verticalSpacing="8dip"
+ />
diff --git a/res/layout/source_list_item.xml b/res/layout/source_list_item.xml
new file mode 100644
index 0000000..c83942e
--- /dev/null
+++ b/res/layout/source_list_item.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<view
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ class="com.android.quicksearchbox.ui.SourceView"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ >
+
+ <ImageView android:id="@+id/source_icon"
+ android:layout_width="48dip"
+ android:layout_height="48dip"
+ android:scaleType="centerInside"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentRight="true"
+ />
+
+ <TextView android:id="@+id/source_label"
+ android:textColor="@android:color/primary_text_light"
+ android:singleLine="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/source_icon"
+ android:layout_centerHorizontal="true"
+ />
+
+</view>
diff --git a/res/layout/source_selector.xml b/res/layout/source_selector.xml
new file mode 100644
index 0000000..3800976
--- /dev/null
+++ b/res/layout/source_selector.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<view
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ class="com.android.quicksearchbox.ui.SourceSelector"
+ android:id="@+id/source_selector"
+ android:layout_width="48dip"
+ android:layout_height="fill_parent"
+ >
+
+ <ImageButton
+ android:id="@+id/source_selector_icon"
+ android:background="@drawable/source_selector_bg"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:scaleType="centerInside"
+ android:focusable="true"
+ android:clickable="true"
+ />
+
+ <ImageView
+ android:id="@+id/source_selector_indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/source_selector_arrow"
+ android:layout_gravity="bottom|right"
+ android:layout_marginBottom="4dip"
+ android:layout_marginRight="4dip"
+ />
+
+</view>
diff --git a/res/layout/suggestion.xml b/res/layout/suggestion.xml
index cad7f8b..3b59c52 100644
--- a/res/layout/suggestion.xml
+++ b/res/layout/suggestion.xml
@@ -21,8 +21,6 @@
android:paddingRight="2dip"
android:layout_width="fill_parent"
android:background="@drawable/suggestion_background"
- android:focusable="true"
- android:clickable="true"
android:layout_height="56dip" >
<!-- Icons come first in the layout, since their placement doesn't depend on
diff --git a/res/layout/tab_indicator.xml b/res/layout/tab_indicator.xml
deleted file mode 100644
index c1a961d..0000000
--- a/res/layout/tab_indicator.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- TODO: We actually want -3dip margins, but for some reason,
- the margin becomes 1dip too wide, so we need to specify -4dip. -->
-<view xmlns:android="http://schemas.android.com/apk/res/android"
- class="com.android.quicksearchbox.ui.TabHandleView"
- android:layout_width="0dip"
- android:layout_height="40dip"
- android:layout_weight="1"
- android:layout_marginLeft="-4dip"
- android:layout_marginRight="-4dip"
- android:background="@drawable/tab_indicator_bg">
-
- <ImageView android:id="@+id/tab_icon"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:scaleType="centerInside"
- />
-
-</view>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3b7fc89..7bb4ac2 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -21,6 +21,9 @@
<string name="search_widget">Quick Search Box</string>
<string name="search_widget_hint">Quick Search Box</string>
+ <!-- Source selector -->
+ <string name="global_search_label">Everything</string>
+
<!-- Search settings stuff -->
<!-- Search settings menu item -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 14bdd8d..27637f1 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -19,4 +19,11 @@
<item name="android:windowAnimationStyle">@null</item>
</style>
+ <style name="Theme.SelectSearchSource" parent="@android:style/Theme.Light.Panel">
+ <item name="android:windowFrame">@null</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:windowContentOverlay">@null</item>
+ </style>
+
</resources>
diff --git a/src/com/android/quicksearchbox/SearchActivity.java b/src/com/android/quicksearchbox/SearchActivity.java
index 93d28d2..de8298a 100644
--- a/src/com/android/quicksearchbox/SearchActivity.java
+++ b/src/com/android/quicksearchbox/SearchActivity.java
@@ -16,6 +16,7 @@
package com.android.quicksearchbox;
+import com.android.quicksearchbox.ui.SourceSelector;
import com.android.quicksearchbox.ui.SuggestionClickListener;
import com.android.quicksearchbox.ui.SuggestionViewFactory;
import com.android.quicksearchbox.ui.SuggestionsAdapter;
@@ -23,6 +24,8 @@
import android.app.Activity;
import android.app.SearchManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
import android.content.Intent;
import android.database.DataSetObserver;
import android.graphics.Rect;
@@ -40,17 +43,13 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageButton;
-import android.widget.ProgressBar;
-import android.widget.ScrollView;
// TODO: restore user query when using dpad to move up from top suggestion
-// TODO: focus tab handle on dpad down from query text view?
// TODO: nicer progress animation. one per source?
// TODO: handle long clicks
// TODO: support action keys
// TODO: support IME search action
// TODO: allow typing everywhere in the UI
-// TODO: Show tabs at bottom too
// TODO: don't show new results until there is at least one, or it's done
// TODO: add timeout for source queries
@@ -77,29 +76,39 @@
public final static String INTENT_ACTION_SEARCH_SETTINGS
= "android.search.action.SEARCH_SETTINGS";
+ private static final int REQUEST_SELECT_SOURCE = 0;
+
protected SuggestionsAdapter mSuggestionsAdapter;
protected EditText mQueryTextView;
- protected ScrollView mSuggestionsScrollView;
protected SuggestionsView mSuggestionsView;
protected ImageButton mSearchGoButton;
protected ImageButton mVoiceSearchButton;
- protected ProgressBar mProgressBar;
+ protected SourceSelector mSourceSelector;
private Launcher mLauncher;
- private boolean mUpdateSuggestions = true;
- private String mUserQuery = "";
- private boolean mSelectAll = false;
+ private Source mSource;
+ private boolean mUpdateSuggestions;
+ private Bundle mAppSearchData;
+ private String mUserQuery;
+ private boolean mSelectAll;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
if (DBG) Log.d(TAG, "onCreate()");
- // TODO: Use savedInstanceState to restore state
super.onCreate(savedInstanceState);
+
+ // TODO: Use savedInstanceState to restore state
+ mSource = null;
+ mUpdateSuggestions = true;
+ mAppSearchData = null;
+ mUserQuery = "";
+ mSelectAll = false;
+
setContentView(R.layout.search_bar);
Config config = getConfig();
@@ -109,33 +118,24 @@
.setInitialSourceResultWaitMillis(config.getInitialSourceResultWaitMillis());
mSuggestionsAdapter
.setSourceResultPublishDelayMillis(config.getSourceResultPublishDelayMillis());
- mSuggestionsAdapter.setSources(getSuggestionsProvider().getOrderedSources());
mQueryTextView = (EditText) findViewById(R.id.search_src_text);
- mSuggestionsScrollView = (ScrollView) findViewById(R.id.suggestions_scroll);
mSuggestionsView = (SuggestionsView) findViewById(R.id.suggestions);
mSuggestionsView.setSuggestionClickListener(new ClickHandler());
mSuggestionsView.setInteractionListener(new InputMethodCloser());
- mSuggestionsView.setOnKeyListener(new SuggestionsListKeyListener());
- mSuggestionsView.setAdapter(mSuggestionsAdapter);
+ mSuggestionsView.setOnKeyListener(new SuggestionsViewKeyListener());
mSearchGoButton = (ImageButton) findViewById(R.id.search_go_btn);
mVoiceSearchButton = (ImageButton) findViewById(R.id.search_voice_btn);
-
- Bundle appSearchData = null;
+ mSourceSelector = (SourceSelector) findViewById(R.id.source_selector);
Intent intent = getIntent();
// getIntent() currently always returns non-null, but the API does not guarantee
// that it always will.
if (intent != null) {
- String initialQuery = intent.getStringExtra(SearchManager.QUERY);
- if (!TextUtils.isEmpty(initialQuery)) {
- mUserQuery = initialQuery;
- }
- // TODO: Declare an intent extra for selectAll
- appSearchData = intent.getBundleExtra(SearchManager.APP_DATA);
+ setupFromIntent(intent);
}
- mLauncher = new Launcher(this, appSearchData);
+ mLauncher = new Launcher(this, mAppSearchData);
mVoiceSearchButton.setVisibility(
mLauncher.isVoiceSearchAvailable() ? View.VISIBLE : View.GONE);
@@ -148,6 +148,52 @@
mVoiceSearchButton.setOnClickListener(new VoiceSearchButtonClickListener());
mVoiceSearchButton.setOnKeyListener(new ButtonsKeyListener());
+
+ // Do this at the end, to avoid updating the list view when setSource()
+ // is called.
+ mSuggestionsView.setAdapter(mSuggestionsAdapter);
+
+ // Must be done after the source is set
+ mSourceSelector.setSearchInfo(new SearchInfo());
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_SELECT_SOURCE) {
+ if (resultCode == RESULT_OK && data != null) {
+ setupFromIntent(data);
+ }
+ }
+ }
+
+ private void setupFromIntent(Intent intent) {
+ if (DBG) Log.d(TAG, "setupFromIntent(" + intent.toUri(0) + ")");
+ ComponentName sourceName = SourceSelector.getGlobalSearchComponent(intent);
+ setSource(sourceName);
+
+ String initialQuery = intent.getStringExtra(SearchManager.QUERY);
+ if (!TextUtils.isEmpty(initialQuery)) {
+ mUserQuery = initialQuery;
+ }
+ // TODO: Declare an intent extra for selectAll
+ mAppSearchData = intent.getBundleExtra(SearchManager.APP_DATA);
+ }
+
+ private void setSource(ComponentName sourceName) {
+ if (sourceName == null) {
+ mSource = null;
+ mSuggestionsAdapter.setSource(null);
+ } else {
+ SourceLookup sources = getSources();
+ mSource = sources.getSourceByComponentName(sourceName);
+ if (mSource != null) {
+ mSuggestionsAdapter.setSource(sourceName);
+ } else {
+ Log.w(TAG, "Unknown source " + sourceName);
+ mSuggestionsAdapter.setSource(null);
+ }
+ }
+ mSourceSelector.update();
}
private QsbApplication getQsbApplication() {
@@ -158,6 +204,10 @@
return getQsbApplication().getConfig();
}
+ private SourceLookup getSources() {
+ return getQsbApplication().getSources();
+ }
+
private ShortcutRepository getShortcutRepository() {
return getQsbApplication().getShortcutRepository();
}
@@ -218,7 +268,7 @@
return true;
}
- private String getQuery() {
+ protected String getQuery() {
CharSequence q = mQueryTextView.getText();
return q == null ? "" : q.toString();
}
@@ -382,17 +432,6 @@
mSearchGoButton.setImageResource(R.drawable.ic_btn_search);
}
- private void scrollPastTabs() {
- // TODO: Right after starting, the scroll view hasn't been measured,
- // so it doesn't know whether its contents are tall enough to scroll.
- int yOffset = mSuggestionsView.getTabHeight();
- mSuggestionsScrollView.scrollTo(0, yOffset);
- if (DBG) {
- Log.d(TAG, "After scrollTo(0," + yOffset + "), scrollY="
- + mSuggestionsScrollView.getScrollY());
- }
- }
-
private void updateSuggestions(String query) {
LatencyTracker latency = new LatencyTracker(TAG);
Suggestions suggestions = getSuggestionsProvider().getSuggestions(query);
@@ -404,7 +443,6 @@
stopSearchProgress();
}
mSuggestionsAdapter.setSuggestions(suggestions);
- scrollPastTabs();
latency.addEvent("shortcuts_shown");
long userVisibleLatency = latency.getUserVisibleLatency();
if (DBG) {
@@ -465,10 +503,8 @@
/**
* Handles key events on the suggestions list view.
- *
- * TODO: This actually never gets called, don't know why yet.
*/
- private class SuggestionsListKeyListener implements View.OnKeyListener {
+ private class SuggestionsViewKeyListener implements View.OnKeyListener {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
SuggestionPosition suggestion = getSelectedSuggestion();
@@ -487,16 +523,20 @@
}
private class ClickHandler implements SuggestionClickListener {
- public void onIconClicked(SuggestionPosition suggestion, Rect rect) {
- launchSuggestionSecondary(suggestion, rect);
- }
-
- public void onItemClicked(SuggestionPosition suggestion) {
+ public void onSuggestionClicked(SuggestionPosition suggestion) {
launchSuggestion(suggestion);
}
- public void onItemSelected(SuggestionPosition suggestion) {
- onSuggestionSelected(suggestion);
+ public boolean onSuggestionLongClicked(SuggestionPosition suggestion) {
+ return SearchActivity.this.onSuggestionLongClicked(suggestion);
+ }
+
+ public void onSuggestionSelected(SuggestionPosition suggestion) {
+ SearchActivity.this.onSuggestionSelected(suggestion);
+ }
+
+ public void onSuggestionIconClicked(SuggestionPosition suggestion, Rect rect) {
+ launchSuggestionSecondary(suggestion, rect);
}
}
@@ -535,4 +575,26 @@
}
}
}
+
+ private class SearchInfo implements SourceSelector.SearchInfo {
+ public Bundle getAppSearchData() {
+ return mAppSearchData;
+ }
+ public Drawable getSourceIcon() {
+ if (mSource == null) {
+ return getSuggestionViewFactory().getGlobalSearchIcon();
+ } else {
+ return mSource.getSourceIcon();
+ }
+ }
+ public String getQuery() {
+ return SearchActivity.this.getQuery();
+ }
+ public ComponentName getSourceName() {
+ return mSource == null ? null : mSource.getComponentName();
+ }
+ public void startActivity(Intent intent) throws ActivityNotFoundException {
+ startActivityForResult(intent, REQUEST_SELECT_SOURCE);
+ }
+ }
}
diff --git a/src/com/android/quicksearchbox/SelectSearchSourceActivity.java b/src/com/android/quicksearchbox/SelectSearchSourceActivity.java
new file mode 100644
index 0000000..d3e7dac
--- /dev/null
+++ b/src/com/android/quicksearchbox/SelectSearchSourceActivity.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quicksearchbox;
+
+import com.android.quicksearchbox.ui.SourceSelector;
+import com.android.quicksearchbox.ui.SourcesAdapter;
+import com.android.quicksearchbox.ui.SuggestionViewFactory;
+
+import android.app.Activity;
+import android.app.SearchManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.GridView;
+
+
+/**
+ * Search source selection activity.
+ */
+public class SelectSearchSourceActivity extends Activity {
+
+ private static final boolean DBG = true;
+ private static final String TAG = "QSB.SelectSearchSourceActivity";
+
+ private GridView mSourceList;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ if (DBG) Log.d(TAG, "onCreate()");
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+ Rect target = intent.getSourceBounds();
+ if (target == null) {
+ Log.w(TAG, "No source bounds in intent.");
+ target = new Rect(0,0,0,0);
+ }
+ if (DBG) Log.d(TAG, "Source bounds: " + target);
+
+ Window window = getWindow();
+ WindowManager windowManager = window.getWindowManager();
+ int screenWidth = windowManager.getDefaultDisplay().getWidth();
+ int screenHeight = windowManager.getDefaultDisplay().getHeight();
+ if (DBG) Log.d(TAG, "Screen size: " + screenWidth + "x" + screenHeight);
+
+ WindowManager.LayoutParams lp = window.getAttributes();
+ // TODO: Figure out formula to position the window with the point at the center
+ // bottom of the source selector, regardless of where that is on the screen.
+ lp.x = 0; // target.right;
+ lp.y = 0; // target.bottom;
+ lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
+ lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ lp.gravity = Gravity.TOP | Gravity.LEFT;
+ lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+ window.setAttributes(lp);
+ if (DBG) Log.d(TAG, "Window params: " + lp);
+
+ setContentView(R.layout.select_search_source);
+ mSourceList = (GridView) findViewById(R.id.source_list);
+ mSourceList.setAdapter(new SourcesAdapter(getViewFactory(), getSuggestionsProvider()));
+ mSourceList.setOnItemClickListener(new SourceClickListener());
+ // TODO: for some reason, putting this in the XML layout instead makes
+ // the list items unclickable.
+ mSourceList.setFocusable(true);
+ }
+
+ private QsbApplication getQsbApplication() {
+ return (QsbApplication) getApplication();
+ }
+
+ private SuggestionsProvider getSuggestionsProvider() {
+ return getQsbApplication().getSuggestionsProvider();
+ }
+
+ private SuggestionViewFactory getViewFactory() {
+ return getQsbApplication().getSuggestionViewFactory();
+ }
+
+ protected void selectSource(Source source) {
+ // If a new source was selected, start QSB with that source.
+ // If the old source was selected, just finish.
+ if (!isPreviousSource(source)) {
+ switchSource(source);
+ }
+ finish();
+ }
+
+ private boolean isPreviousSource(Source source) {
+ Intent intent = getIntent();
+ ComponentName previousSource = SourceSelector.getGlobalSearchComponent(intent);
+ if (source == null) return previousSource == null;
+ return source.getComponentName().equals(previousSource);
+ }
+
+ private void switchSource(Source source) {
+ Intent selectIntent = getIntent();
+ String query = selectIntent.getStringExtra(SearchManager.QUERY);
+ Bundle appSearchData = selectIntent.getBundleExtra(SearchManager.APP_DATA);
+
+ Intent searchIntent = new Intent(this, SearchActivity.class);
+ if (source != null) {
+ SourceSelector.setGlobalSearchComponent(searchIntent, source.getComponentName());
+ }
+ if (query != null) {
+ searchIntent.putExtra(SearchManager.QUERY, query);
+ }
+ if (appSearchData != null) {
+ searchIntent.putExtra(SearchManager.APP_DATA, appSearchData);
+ }
+
+ if (getCallingPackage() == null) {
+ // startActivityForResult() was not used, start a new QSB activity
+ try {
+ startActivity(searchIntent);
+ } catch (ActivityNotFoundException ex) {
+ Log.e(TAG, "Couldn't start QSB: " + ex);
+ }
+ } else {
+ // startActivityForResult() was used, return result instead of starting a new activity
+ setResult(RESULT_OK, searchIntent);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ // Dismiss source selector on touch outside.
+ if (event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(event)) {
+ finish();
+ return true;
+ }
+ // TODO: select source on ACTION_UP, to allow press and drag on source selector.
+ return super.onTouchEvent(event);
+ }
+
+ private boolean isOutOfBounds(MotionEvent event) {
+ final int x = (int) event.getX();
+ final int y = (int) event.getY();
+ final int slop = ViewConfiguration.get(this).getScaledWindowTouchSlop();
+ final View decorView = getWindow().getDecorView();
+ return (x < -slop) || (y < -slop)
+ || (x > (decorView.getWidth() + slop))
+ || (y > (decorView.getHeight() + slop));
+ }
+
+ private class SourceClickListener implements AdapterView.OnItemClickListener {
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ Source source = (Source) parent.getItemAtPosition(position);
+ selectSource(source);
+ }
+ }
+}
diff --git a/src/com/android/quicksearchbox/SuggestionsView.java b/src/com/android/quicksearchbox/SuggestionsView.java
deleted file mode 100644
index e69de29..0000000
--- a/src/com/android/quicksearchbox/SuggestionsView.java
+++ /dev/null
diff --git a/src/com/android/quicksearchbox/ui/SourceSelector.java b/src/com/android/quicksearchbox/ui/SourceSelector.java
new file mode 100644
index 0000000..492bd54
--- /dev/null
+++ b/src/com/android/quicksearchbox/ui/SourceSelector.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quicksearchbox.ui;
+
+import com.android.quicksearchbox.R;
+
+import android.app.SearchManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+
+/**
+ * Source indicator widget. Touching the widget brings up the source selection activity.
+ */
+public class SourceSelector extends FrameLayout {
+
+ private static final boolean DBG = true;
+ private static final String TAG = "QSB.SourceSelector";
+
+ // TODO: Move to android.content.Intent?
+ private static final String ACTION_SELECT_SEARCH_SOURCE
+ = "android.intent.action.SELECT_SEARCH_SOURCE";
+
+ private static final String GLOBAL_SEARCH_COMPONENT
+ = "global_search_component";
+
+ private ImageButton mIconView;
+
+ private SearchInfo mSearchInfo;
+
+ public SourceSelector(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SourceSelector(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mIconView = (ImageButton) findViewById(R.id.source_selector_icon);
+ mIconView.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ startSelectorActivity();
+ }
+ });
+ }
+
+ public void update() {
+ if (mSearchInfo == null) {
+ mIconView.setImageDrawable(null);
+ } else {
+ mIconView.setImageDrawable(mSearchInfo.getSourceIcon());
+ }
+ }
+
+ public void setSearchInfo(SearchInfo searchInfo) {
+ mSearchInfo = searchInfo;
+ update();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ startSelectorActivity();
+ // TODO: This breaks clicks.
+ return true;
+ }
+ return super.onTouchEvent(event);
+ }
+
+ protected void startSelectorActivity() {
+ String query = null;
+ Bundle appSearchData = null;
+ ComponentName source = null;
+
+ if (mSearchInfo != null) {
+ source = mSearchInfo.getSourceName();
+ query = mSearchInfo.getQuery();
+ appSearchData = mSearchInfo.getAppSearchData();
+ }
+
+ Rect rect = Util.getOnScreenRect(this);
+ Intent intent = new Intent(ACTION_SELECT_SEARCH_SOURCE);
+ intent.setSourceBounds(rect);
+ if (source != null) {
+ setGlobalSearchComponent(intent, source);
+ }
+ if (query != null) {
+ intent.putExtra(SearchManager.QUERY, query);
+ }
+ if (appSearchData != null) {
+ intent.putExtra(SearchManager.APP_DATA, appSearchData);
+ }
+ try {
+ if (mSearchInfo != null) {
+ mSearchInfo.startActivity(intent);
+ } else {
+ getContext().startActivity(intent);
+ }
+ } catch (ActivityNotFoundException ex) {
+ Log.e(TAG, "Could open source selector: " + ex);
+ }
+ }
+
+ public static ComponentName getGlobalSearchComponent(Intent intent) {
+ String name = intent.getStringExtra(GLOBAL_SEARCH_COMPONENT);
+ return name == null ? null : ComponentName.unflattenFromString(name);
+ }
+
+ public static void setGlobalSearchComponent(Intent intent, ComponentName name) {
+ if (name != null) {
+ intent.putExtra(GLOBAL_SEARCH_COMPONENT, name.flattenToShortString());
+ }
+ }
+
+ public interface SearchInfo {
+ ComponentName getSourceName();
+ Drawable getSourceIcon();
+ String getQuery();
+ Bundle getAppSearchData();
+ void startActivity(Intent intent) throws ActivityNotFoundException;
+ }
+}
diff --git a/src/com/android/quicksearchbox/ui/SourceView.java b/src/com/android/quicksearchbox/ui/SourceView.java
new file mode 100644
index 0000000..e9a8128
--- /dev/null
+++ b/src/com/android/quicksearchbox/ui/SourceView.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quicksearchbox.ui;
+
+import com.android.quicksearchbox.R;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+
+/**
+ * A source in the source selection list.
+ */
+public class SourceView extends RelativeLayout {
+
+ private static final boolean DBG = true;
+ private static final String TAG = "QSB.SourceView";
+
+ private ImageView mIcon;
+ private TextView mLabel;
+
+ public SourceView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SourceView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mIcon = (ImageView) findViewById(R.id.source_icon);
+ mLabel = (TextView) findViewById(R.id.source_label);
+ }
+
+ public void setLabel(CharSequence label) {
+ mLabel.setText(label);
+ }
+
+ public void setIcon(Drawable icon) {
+ mIcon.setImageDrawable(icon);
+ }
+}
diff --git a/src/com/android/quicksearchbox/ui/SourcesAdapter.java b/src/com/android/quicksearchbox/ui/SourcesAdapter.java
new file mode 100644
index 0000000..2286826
--- /dev/null
+++ b/src/com/android/quicksearchbox/ui/SourcesAdapter.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quicksearchbox.ui;
+
+import com.android.quicksearchbox.Source;
+import com.android.quicksearchbox.SuggestionsProvider;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+import java.util.ArrayList;
+
+/**
+ * Adapter for showing a list of sources in the source selection activity.
+ */
+public class SourcesAdapter extends BaseAdapter {
+
+ private final SuggestionViewFactory mViewFactory;
+
+ private final SuggestionsProvider mProvider;
+
+ private ArrayList<Source> mEnabledSources;
+
+ public SourcesAdapter(SuggestionViewFactory viewFactory, SuggestionsProvider provider) {
+ mViewFactory = viewFactory;
+ mProvider = provider;
+ updateSources();
+ }
+
+ private void updateSources() {
+ mEnabledSources = mProvider.getOrderedSources();
+ }
+
+ public int getCount() {
+ return 1 + mEnabledSources.size();
+ }
+
+ public Source getItem(int position) {
+ if (position == 0) {
+ return null;
+ } else {
+ return mEnabledSources.get(position - 1);
+ }
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ SourceView view = (SourceView) convertView;
+ if (view == null) {
+ view = mViewFactory.createSourceView(parent);
+ }
+ Source source = getItem(position);
+ if (source == null) {
+ view.setIcon(mViewFactory.getGlobalSearchIcon());
+ view.setLabel(mViewFactory.getGlobalSearchLabel());
+ } else {
+ view.setIcon(source.getSourceIcon());
+ view.setLabel(source.getLabel());
+ }
+ return view;
+ }
+}
diff --git a/src/com/android/quicksearchbox/ui/SuggestionClickListener.java b/src/com/android/quicksearchbox/ui/SuggestionClickListener.java
index 6bc5360..25296f9 100644
--- a/src/com/android/quicksearchbox/ui/SuggestionClickListener.java
+++ b/src/com/android/quicksearchbox/ui/SuggestionClickListener.java
@@ -24,7 +24,8 @@
* Listener interface for clicks on suggestions.
*/
public interface SuggestionClickListener {
- void onIconClicked(SuggestionPosition suggestion, Rect rect);
- void onItemClicked(SuggestionPosition suggestion);
- void onItemSelected(SuggestionPosition suggestion);
+ void onSuggestionClicked(SuggestionPosition suggestion);
+ boolean onSuggestionLongClicked(SuggestionPosition suggestion);
+ void onSuggestionSelected(SuggestionPosition suggestion);
+ void onSuggestionIconClicked(SuggestionPosition suggestion, Rect rect);
}
diff --git a/src/com/android/quicksearchbox/ui/SuggestionCursorAdapter.java b/src/com/android/quicksearchbox/ui/SuggestionCursorAdapter.java
deleted file mode 100644
index 0805b6a..0000000
--- a/src/com/android/quicksearchbox/ui/SuggestionCursorAdapter.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quicksearchbox.ui;
-
-import com.android.quicksearchbox.SuggestionCursor;
-
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-
-/**
- * List adapter backed by a {@link SuggestionCursor}.
- */
-public class SuggestionCursorAdapter extends BaseAdapter {
-
- private static final boolean DBG = true;
- private static final String TAG = "QSB.SuggestionCursorAdapter";
-
- private final SuggestionViewFactory mViewFactory;
-
- private SuggestionCursor mCursor;
-
- public SuggestionCursorAdapter(SuggestionViewFactory viewFactory) {
- mViewFactory = viewFactory;
- }
-
- /**
- * Replace the cursor.
- *
- * This does not close the old cursor. We rely on {@link SuggestionsAdapter}
- * for that.
- */
- public void changeCursor(SuggestionCursor newCursor) {
- if (DBG) Log.d(TAG, "changeCursor(" + newCursor + ")");
- if (newCursor == mCursor) {
- return;
- }
- mCursor = newCursor;
- if (mCursor != null) {
- // TODO: Register observers here to watch for
- // changes in the cursor, e.g. shortcut refreshes?
- notifyDataSetChanged();
- } else {
- notifyDataSetInvalidated();
- }
- }
-
- public int getCount() {
- return mCursor == null ? 0 : mCursor.getCount();
- }
-
- public Object getItem(int position) {
- // Unused
- return null;
- }
-
- public long getItemId(int position) {
- return position; // We don't have any IDs for suggestions.
- }
-
- public SuggestionView getView(int position, View convertView, ViewGroup parent) {
- if (DBG) Log.d(TAG, "getView(" + position + ")");
- if (mCursor == null) {
- throw new IllegalStateException("getView() called with null cursor");
- }
- SuggestionView view;
- if (convertView == null) {
- view = mViewFactory.createSuggestionView(parent);
- } else {
- view = (SuggestionView) convertView;
- }
- mCursor.moveTo(position);
- view.bindAsSuggestion(mCursor);
- return view;
- }
-
-}
diff --git a/src/com/android/quicksearchbox/ui/SuggestionListView.java b/src/com/android/quicksearchbox/ui/SuggestionListView.java
deleted file mode 100644
index d8246c5..0000000
--- a/src/com/android/quicksearchbox/ui/SuggestionListView.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quicksearchbox.ui;
-
-import com.android.quicksearchbox.SuggestionPosition;
-
-import android.content.Context;
-import android.database.DataSetObserver;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.widget.LinearLayout;
-
-import java.util.ArrayList;
-
-/**
- * View for a list of suggestions.
- */
-public class SuggestionListView extends LinearLayout {
-
- private static final boolean DBG = true;
- private static final String TAG = "QSB.SuggestionListView";
-
- private static final int RECYCLING_BIN_CAPACITY = 20;
-
- private DataSetObserver mDataSetObserver;
-
- private SuggestionCursorAdapter mAdapter;
-
- private SuggestionClickListener mSuggestionClickListener;
-
- private final ArrayList<SuggestionView> mRecyclingBin
- = new ArrayList<SuggestionView>(RECYCLING_BIN_CAPACITY);
-
- public SuggestionListView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public SuggestionListView(Context context) {
- super(context);
- }
-
- public void setSuggestionClickListener(SuggestionClickListener clickListener) {
- mSuggestionClickListener = clickListener;
- }
-
- public SuggestionCursorAdapter getAdapter() {
- return mAdapter;
- }
-
- public int getCount() {
- return mAdapter == null ? 0 : mAdapter.getCount();
- }
-
- public int getSelectedPosition() {
- SuggestionView view = (SuggestionView) getFocusedChild();
- if (view == null) return -1;
- return indexOfChild(view); // TODO: this is a linear search, not great
- }
-
- public SuggestionPosition getSelectedSuggestion() {
- SuggestionView view = (SuggestionView) getFocusedChild();
- if (view == null) return null;
- return view.getSuggestionPosition();
- }
-
- public void setAdapter(SuggestionCursorAdapter adapter) {
- if (mAdapter == adapter) {
- return;
- }
- if (mDataSetObserver == null) {
- mDataSetObserver = new AdapterObserver();
- }
- if (mAdapter != null) {
- mAdapter.unregisterDataSetObserver(mDataSetObserver);
- }
- mAdapter = adapter;
- if (mAdapter != null) {
- mAdapter.registerDataSetObserver(mDataSetObserver);
- }
- onDataSetChanged();
- }
-
- @Override
- public void removeAllViews() {
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- addToRecyclingBin((SuggestionView) getChildAt(i));
- }
- super.removeAllViews();
- }
-
- private void addToRecyclingBin(SuggestionView view) {
- if (mRecyclingBin.size() < RECYCLING_BIN_CAPACITY) {
- if (DBG) Log.d(TAG, "Storing SuggestionView for recycling. parent=" + view.getParent());
- mRecyclingBin.add(view);
- // TODO: clear old drawables etc to free memory?
- }
- }
-
- private SuggestionView getFromRecyclingBin() {
- int size = mRecyclingBin.size();
- if (size > 0) {
- if (DBG) Log.d(TAG, "Recycling SuggestionView.");
- return mRecyclingBin.remove(size - 1);
- } else {
- return null;
- }
- }
-
- protected void onDataSetChanged() {
- removeAllViews();
- int count = getCount();
- for (int i = 0; i < count; i++) {
- SuggestionView recycled = getFromRecyclingBin();
- SuggestionView view = mAdapter.getView(i, recycled, this);
- view.setSuggestionClickListener(mSuggestionClickListener);
- addView(view);
- }
- }
-
- private class AdapterObserver extends DataSetObserver {
- @Override
- public void onChanged() {
- onDataSetChanged();
- }
- }
-
-}
diff --git a/src/com/android/quicksearchbox/ui/SuggestionView.java b/src/com/android/quicksearchbox/ui/SuggestionView.java
index ebdab95..80a4976 100644
--- a/src/com/android/quicksearchbox/ui/SuggestionView.java
+++ b/src/com/android/quicksearchbox/ui/SuggestionView.java
@@ -21,7 +21,6 @@
import com.android.quicksearchbox.SuggestionPosition;
import android.content.Context;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -36,8 +35,7 @@
* sources, and suggestions under each source.
*
*/
-public class SuggestionView extends RelativeLayout
- implements View.OnClickListener, View.OnFocusChangeListener {
+public class SuggestionView extends RelativeLayout implements View.OnClickListener {
private static final boolean DBG = true;
private static final String TAG = "QSB.SuggestionView";
@@ -79,6 +77,7 @@
mIcon2 = (ImageView) findViewById(R.id.icon2);
}
+ // TODO: never gets called
public void setSuggestionClickListener(SuggestionClickListener listener) {
mSuggestionClickListener = listener;
}
@@ -88,20 +87,10 @@
return;
}
if (v == this) {
- mSuggestionClickListener.onItemClicked(getSuggestionPosition());
+ mSuggestionClickListener.onSuggestionClicked(getSuggestionPosition());
} else if (v == mIcon1) {
- mSuggestionClickListener.onIconClicked(getSuggestionPosition(),
- getOnScreenRect(mIcon1));
- }
- }
-
- public void onFocusChange(View v, boolean hasFocus) {
- if (DBG) Log.d(TAG, "onFocusChange(" + hasFocus + ")");
- if (mSuggestionClickListener == null) {
- return;
- }
- if (hasFocus) {
- mSuggestionClickListener.onItemSelected(getSuggestionPosition());
+ mSuggestionClickListener.onSuggestionIconClicked(getSuggestionPosition(),
+ Util.getOnScreenRect(mIcon1));
}
}
@@ -131,11 +120,9 @@
setIcon1(icon1);
setIcon2(icon2);
- setOnClickListener(this);
if (mIcon1 != null) {
mIcon1.setOnClickListener(this);
}
- setOnFocusChangeListener(this);
}
/**
@@ -176,15 +163,4 @@
}
}
- private Rect getOnScreenRect(View view) {
- int[] location = new int[2];
- view.getLocationOnScreen(location);
- Rect rect = new Rect();
- rect.left = location[0];
- rect.top = location[1];
- rect.right = rect.left + view.getWidth();
- rect.bottom = rect.top + view.getHeight();
- return rect;
- }
-
}
diff --git a/src/com/android/quicksearchbox/ui/SuggestionViewFactory.java b/src/com/android/quicksearchbox/ui/SuggestionViewFactory.java
index 0eb8b4c..8679c67 100644
--- a/src/com/android/quicksearchbox/ui/SuggestionViewFactory.java
+++ b/src/com/android/quicksearchbox/ui/SuggestionViewFactory.java
@@ -31,23 +31,10 @@
*/
SuggestionView createSuggestionView(ViewGroup parentViewType);
- /**
- * Creates a suggestion list view.
- *
- * @param parentViewType Used to create LayoutParams of the right type.
- */
- SuggestionListView createSuggestionListView(ViewGroup parentViewType);
+ SourceView createSourceView(ViewGroup parentViewType);
- /**
- * Creates a tab handle view.
- *
- * @param parentViewType Used to create LayoutParams of the right type.
- */
- TabHandleView createSuggestionTabView(ViewGroup parentViewType);
+ String getGlobalSearchLabel();
- /**
- * Gets the icon to use for the promoted tab.
- */
- Drawable getPromotedIcon();
+ Drawable getGlobalSearchIcon();
}
diff --git a/src/com/android/quicksearchbox/ui/SuggestionViewInflater.java b/src/com/android/quicksearchbox/ui/SuggestionViewInflater.java
index 512c49a..ab3b1a9 100644
--- a/src/com/android/quicksearchbox/ui/SuggestionViewInflater.java
+++ b/src/com/android/quicksearchbox/ui/SuggestionViewInflater.java
@@ -34,6 +34,8 @@
private final Context mContext;
+ private SuggestionClickListener mSuggestionClickListener;
+
public SuggestionViewInflater(Context context) {
mContext = context;
}
@@ -42,29 +44,33 @@
return (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
+ public void setSuggestionClickListener(SuggestionClickListener listener) {
+ mSuggestionClickListener = listener;
+ }
+
public SuggestionView createSuggestionView(ViewGroup parentViewType) {
if (DBG) Log.d(TAG, "createSuggestionView()");
SuggestionView view = (SuggestionView)
getInflater().inflate(R.layout.suggestion, parentViewType, false);
+ if (mSuggestionClickListener != null) {
+ view.setSuggestionClickListener(mSuggestionClickListener);
+ }
return view;
}
- public SuggestionListView createSuggestionListView(ViewGroup parentViewType) {
- if (DBG) Log.d(TAG, "createSuggestionListView()");
- SuggestionListView view = (SuggestionListView)
- getInflater().inflate(R.layout.suggestion_list, parentViewType, false);
+ public SourceView createSourceView(ViewGroup parentViewType) {
+ if (DBG) Log.d(TAG, "createSourceView()");
+ SourceView view = (SourceView)
+ getInflater().inflate(R.layout.source_list_item, parentViewType, false);
return view;
}
- public TabHandleView createSuggestionTabView(ViewGroup parentViewType) {
- if (DBG) Log.d(TAG, "createSuggestionTabView()");
- TabHandleView view = (TabHandleView)
- getInflater().inflate(R.layout.tab_indicator, parentViewType, false);
- return view;
+ public String getGlobalSearchLabel() {
+ return mContext.getString(R.string.global_search_label);
}
- public Drawable getPromotedIcon() {
- return mContext.getResources().getDrawable(R.drawable.promoted_tab_icon);
+ public Drawable getGlobalSearchIcon() {
+ return mContext.getResources().getDrawable(R.drawable.global_search_source);
}
}
diff --git a/src/com/android/quicksearchbox/ui/SuggestionsAdapter.java b/src/com/android/quicksearchbox/ui/SuggestionsAdapter.java
index 7d6ecc4..e6b50f2 100644
--- a/src/com/android/quicksearchbox/ui/SuggestionsAdapter.java
+++ b/src/com/android/quicksearchbox/ui/SuggestionsAdapter.java
@@ -16,23 +16,21 @@
package com.android.quicksearchbox.ui;
-import com.android.quicksearchbox.Source;
import com.android.quicksearchbox.SuggestionCursor;
+import com.android.quicksearchbox.SuggestionPosition;
import com.android.quicksearchbox.Suggestions;
-import android.database.DataSetObservable;
+import android.content.ComponentName;
import android.database.DataSetObserver;
-import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
-
-import java.util.ArrayList;
+import android.widget.BaseAdapter;
/**
- * Uses a {@link Suggestions} object to back a {@link TabView}.
+ * Uses a {@link Suggestions} object to back a {@link SuggestionsView}.
*/
-public class SuggestionsAdapter implements TabAdapter {
+public class SuggestionsAdapter extends BaseAdapter {
private static final boolean DBG = true;
private static final String TAG = "QSB.SuggestionsAdapter";
@@ -40,16 +38,13 @@
private long mSourceResultPublishDelayMillis;
private long mInitialSourceResultWaitMillis;
- private final DataSetObservable mDataSetObservable = new DataSetObservable();
+ private DataSetObserver mDataSetObserver;
private final SuggestionViewFactory mViewFactory;
- private DataSetObserver mDataSetObserver;
+ private SuggestionCursor mCursor;
- /**
- * The tabs, in their display order.
- */
- private final ArrayList<Tab> mTabs = new ArrayList<Tab>();
+ private ComponentName mSource = null;
private Suggestions mSuggestions;
@@ -67,28 +62,9 @@
mInitialSourceResultWaitMillis = millis;
}
- public int getTabCount() {
- return mTabs.size();
- }
-
- public void setSources(ArrayList<Source> sources) {
- setSuggestions(null);
- mTabs.clear();
- SuggestionCursorAdapter promoted = new SuggestionCursorAdapter(mViewFactory);
- mTabs.add(new PromotedTab(promoted));
- int count = sources.size();
- for (int i = 0; i < count; i++) {
- Source source = sources.get(i);
- // TODO: Each source should specify its own view factory
- SuggestionCursorAdapter adapter = new SuggestionCursorAdapter(mViewFactory);
- mTabs.add(new SourceTab(source, adapter));
- }
- notifyDataSetChanged();
- }
-
public void close() {
setSuggestions(null);
- mTabs.clear();
+ mSource = null;
mClosed = true;
}
@@ -117,53 +93,79 @@
onSuggestionsChanged();
}
- public View getTabContentView(int position, ViewGroup parent) {
- if (DBG) Log.d(TAG, "getTabContent(" + position + ")");
- return getTab(position).getListView(parent);
+ /**
+ * Sets the source whose results are displayed.
+ *
+ * @param source The name of a source, or {@code null} to show
+ * the promoted results.
+ */
+ public void setSource(ComponentName source) {
+ mSource = source;
+ onSuggestionsChanged();
}
- public View getTabHandleView(int position, ViewGroup parent) {
- return getTab(position).getTabHandleView(parent);
+ public int getCount() {
+ return mCursor == null ? 0 : mCursor.getCount();
}
- private Tab getTab(int position) {
- if (mClosed) {
- throw new IllegalStateException("SuggestionsAdapter is closed.");
+ public SuggestionPosition getItem(int position) {
+ if (mCursor == null) return null;
+ return new SuggestionPosition(mCursor, position);
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (DBG) Log.d(TAG, "getView(" + position + ")");
+ if (mCursor == null) {
+ throw new IllegalStateException("getView() called with null cursor");
}
- return mTabs.get(position);
- }
-
- public String getTag(int position) {
- return String.valueOf(position);
- }
-
- public int getTabPosition(String tag) {
- return Integer.parseInt(tag);
- }
-
- public void registerDataSetObserver(DataSetObserver observer) {
- mDataSetObservable.registerObserver(observer);
- }
-
- public void unregisterDataSetObserver(DataSetObserver observer) {
- mDataSetObservable.unregisterObserver(observer);
- }
-
- protected void notifyDataSetChanged() {
- mDataSetObservable.notifyChanged();
- }
-
- protected void notifyDataSetInvalidated() {
- mDataSetObservable.notifyInvalidated();
+ SuggestionView view;
+ if (convertView == null) {
+ view = mViewFactory.createSuggestionView(parent);
+ } else {
+ view = (SuggestionView) convertView;
+ }
+ mCursor.moveTo(position);
+ view.bindAsSuggestion(mCursor);
+ return view;
}
protected void onSuggestionsChanged() {
if (DBG) Log.d(TAG, "onSuggestionsChanged(), mSuggestions=" + mSuggestions);
- // TODO: It's inefficient to change all cursors every time a
- // new one is added to Suggestions, we should get a set of
- // changed ones in the call.
- for (Tab tab : mTabs) {
- tab.update();
+ SuggestionCursor cursor = getCursor();
+ changeCursor(cursor);
+ }
+
+ /**
+ * Gets the cursor for the selected source.
+ */
+ private SuggestionCursor getCursor() {
+ if (mSuggestions == null) return null;
+ if (mSource == null) return mSuggestions.getPromoted();
+ return mSuggestions.getSourceResult(mSource);
+ }
+
+ /**
+ * Replace the cursor.
+ *
+ * This does not close the old cursor. Instead, all the cursors are closed in
+ * {@link #setSuggestions(Suggestions)}.
+ */
+ private void changeCursor(SuggestionCursor newCursor) {
+ if (DBG) Log.d(TAG, "changeCursor(" + newCursor + ")");
+ if (newCursor == mCursor) {
+ return;
+ }
+ mCursor = newCursor;
+ if (mCursor != null) {
+ // TODO: Register observers here to watch for
+ // changes in the cursor, e.g. shortcut refreshes?
+ notifyDataSetChanged();
+ } else {
+ notifyDataSetInvalidated();
}
}
@@ -174,77 +176,4 @@
}
}
- private abstract class Tab {
- private final SuggestionCursorAdapter mAdapter;
-
- public Tab(SuggestionCursorAdapter adapter) {
- mAdapter = adapter;
- }
-
- public SuggestionCursorAdapter getAdapter() {
- return mAdapter;
- }
-
- public void update() {
- if (mSuggestions == null) {
- mAdapter.changeCursor(null);
- } else {
- mAdapter.changeCursor(getCursor(mSuggestions));
- }
- }
-
- protected abstract SuggestionCursor getCursor(Suggestions suggestions);
-
- protected abstract Drawable getIcon();
-
- public View getTabHandleView(ViewGroup parent) {
- TabHandleView view = mViewFactory.createSuggestionTabView(parent);
- view.setIcon(getIcon());
- return view;
- }
-
- public View getListView(ViewGroup parent) {
- if (DBG) Log.d(TAG, "getListView()");
- SuggestionListView view = mViewFactory.createSuggestionListView(parent);
- view.setAdapter(mAdapter);
- return view;
- }
- }
-
- private class PromotedTab extends Tab {
-
- public PromotedTab(SuggestionCursorAdapter adapter) {
- super(adapter);
- }
-
- @Override
- protected SuggestionCursor getCursor(Suggestions suggestions) {
- return suggestions.getPromoted();
- }
-
- @Override
- protected Drawable getIcon() {
- return mViewFactory.getPromotedIcon();
- }
- }
-
- private class SourceTab extends Tab {
- private final Source mSource;
-
- public SourceTab(Source source, SuggestionCursorAdapter adapter) {
- super(adapter);
- mSource = source;
- }
-
- @Override
- protected SuggestionCursor getCursor(Suggestions suggestions) {
- return suggestions.getSourceResult(mSource.getComponentName());
- }
-
- @Override
- protected Drawable getIcon() {
- return mSource.getSourceIcon();
- }
- }
-
}
diff --git a/src/com/android/quicksearchbox/ui/SuggestionsView.java b/src/com/android/quicksearchbox/ui/SuggestionsView.java
index 4c7f824..947eef7 100644
--- a/src/com/android/quicksearchbox/ui/SuggestionsView.java
+++ b/src/com/android/quicksearchbox/ui/SuggestionsView.java
@@ -20,13 +20,16 @@
import android.content.Context;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.MotionEvent;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
/**
- * Shows all suggestions in a tabbed interface.
- *
+ * Holds a list of suggestions.
*/
-public class SuggestionsView extends TabView {
+public class SuggestionsView extends ListView {
private static final boolean DBG = true;
private static final String TAG = "QSB.SuggestionsView";
@@ -39,6 +42,17 @@
super(context, attrs);
}
+ @Override
+ public void onFinishInflate() {
+ super.onFinishInflate();
+ setOnItemClickListener(new ItemClickListener());
+ setOnItemLongClickListener(new ItemLongClickListener());
+ // TODO: the OnItemSelectedListener gets fired by ListView even when
+ // the selection changes without user interaction, e.g. when changing
+ // the cursor. Disabling until we can figure out a way around that.
+ //setOnItemSelectedListener(new ItemSelectedListener());
+ }
+
public void setSuggestionClickListener(SuggestionClickListener listener) {
mSuggestionClickListener = listener;
}
@@ -48,26 +62,21 @@
}
/**
- * Gets the position of the selected suggestion in the currently
- * displayed tab.
+ * Gets the position of the selected suggestion.
*
* @return A 0-based index, or {@code -1} if no suggestion is selected.
*/
public int getSelectedPosition() {
- SuggestionListView view = (SuggestionListView) getCurrentTabView();
- if (view == null) return -1;
- return view.getSelectedPosition();
+ return getSelectedItemPosition();
}
/**
- * Gets the selected suggestion in the currently displayed tab.
+ * Gets the selected suggestion.
*
* @return {@code null} if no suggestion is selected.
*/
public SuggestionPosition getSelectedSuggestion() {
- SuggestionListView view = (SuggestionListView) getCurrentTabView();
- if (view == null) return null;
- return view.getSelectedSuggestion();
+ return (SuggestionPosition) getSelectedItem();
}
@Override
@@ -78,13 +87,6 @@
return super.onInterceptTouchEvent(event);
}
- @Override
- protected SuggestionListView createTabContentView(int position) {
- SuggestionListView view = (SuggestionListView) super.createTabContentView(position);
- view.setSuggestionClickListener(mSuggestionClickListener);
- return view;
- }
-
public interface InteractionListener {
/**
* Called when the user interacts with this view.
@@ -92,4 +94,40 @@
void onInteraction();
}
+ private class ItemClickListener implements AdapterView.OnItemClickListener {
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if (DBG) Log.d(TAG, "onItemClick(" + position + ")");
+ SuggestionView suggestionView = (SuggestionView) view;
+ SuggestionPosition suggestion = suggestionView.getSuggestionPosition();
+ if (mSuggestionClickListener != null) {
+ mSuggestionClickListener.onSuggestionClicked(suggestion);
+ }
+ }
+ }
+
+ private class ItemLongClickListener implements AdapterView.OnItemLongClickListener {
+ public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
+ if (DBG) Log.d(TAG, "onItemLongClick(" + position + ")");
+ SuggestionView suggestionView = (SuggestionView) view;
+ SuggestionPosition suggestion = suggestionView.getSuggestionPosition();
+ if (mSuggestionClickListener != null) {
+ return mSuggestionClickListener.onSuggestionLongClicked(suggestion);
+ }
+ return false;
+ }
+ }
+
+ private class ItemSelectedListener implements AdapterView.OnItemSelectedListener {
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ if (DBG) Log.d(TAG, "onItemSelected(" + position + ")");
+ SuggestionView suggestionView = (SuggestionView) view;
+ SuggestionPosition suggestion = suggestionView.getSuggestionPosition();
+ if (mSuggestionClickListener != null) {
+ mSuggestionClickListener.onSuggestionSelected(suggestion);
+ }
+ }
+
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ }
}
diff --git a/src/com/android/quicksearchbox/ui/TabAdapter.java b/src/com/android/quicksearchbox/ui/TabAdapter.java
deleted file mode 100644
index 9d5fe64..0000000
--- a/src/com/android/quicksearchbox/ui/TabAdapter.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quicksearchbox.ui;
-
-import android.database.DataSetObserver;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Provides content to a {@link TabView}.
- */
-public interface TabAdapter {
-
- /**
- * Gets the number of tabs.
- */
- int getTabCount();
-
- /**
- * Gets a unique string identifying a tab.
- *
- * @param position Tab position.
- */
- String getTag(int position);
-
- /**
- * Gets the position of a tab given its tag.
- */
- int getTabPosition(String tag);
-
- /**
- * Gets the view used for the tab handle.
- */
- View getTabHandleView(int position, ViewGroup parent);
-
- /**
- * Gets the view used for the tab content.
- */
- View getTabContentView(int position, ViewGroup parent);
-
- /**
- * Closes the tab adapter, releasing any resources that it may be using.
- */
- void close();
-
- /**
- * Register an observer that is called when changes happen to the data used by this adapter.
- *
- * @param observer the object that gets notified when the data set changes.
- */
- void registerDataSetObserver(DataSetObserver observer);
-
- /**
- * Unregister an observer that has previously been registered with this
- * adapter via {@link #registerDataSetObserver}.
- *
- * @param observer the object to unregister.
- */
- void unregisterDataSetObserver(DataSetObserver observer);
-
-}
diff --git a/src/com/android/quicksearchbox/ui/TabHandleView.java b/src/com/android/quicksearchbox/ui/TabHandleView.java
deleted file mode 100644
index 45479d9..0000000
--- a/src/com/android/quicksearchbox/ui/TabHandleView.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quicksearchbox.ui;
-
-import com.android.quicksearchbox.R;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-import android.widget.RelativeLayout;
-
-/**
- * Suggestion tab handle view.
- */
-public class TabHandleView extends RelativeLayout {
-
- public TabHandleView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public TabHandleView(Context context) {
- super(context);
- }
-
- public void setIcon(Drawable icon) {
- ImageView imageView = (ImageView) findViewById(R.id.tab_icon);
- imageView.setImageDrawable(icon);
- }
-
-}
diff --git a/src/com/android/quicksearchbox/ui/TabView.java b/src/com/android/quicksearchbox/ui/TabView.java
deleted file mode 100644
index 0e7f33a..0000000
--- a/src/com/android/quicksearchbox/ui/TabView.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quicksearchbox.ui;
-
-import android.content.Context;
-import android.database.DataSetObserver;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.TabHost;
-
-/**
- * A wrapper around {@link TabHost}.
- */
-public class TabView extends TabHost {
-
- private static final boolean DBG = true;
- private static final String TAG = "QSB.TabView";
-
- private DataSetObserver mDataSetObserver;
-
- private TabContentFactory mTabContentFactory;
-
- private TabAdapter mAdapter;
-
- private GestureDetector mGestureDetector;
-
- public TabView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mGestureDetector = new GestureDetector(getContext(), new GestureListener());
- }
-
- public TabAdapter getAdapter() {
- return mAdapter;
- }
-
- public void setAdapter(TabAdapter adapter) {
- if (mDataSetObserver == null) {
- mDataSetObserver = createDataSetObserver();
- }
- if (adapter == mAdapter) {
- return;
- }
- if (mAdapter != null) {
- mAdapter.unregisterDataSetObserver(mDataSetObserver);
- mAdapter.close();
- }
- mAdapter = adapter;
- if (mAdapter != null) {
- mAdapter.registerDataSetObserver(mDataSetObserver);
- }
- onDataSetChanged();
- }
-
- protected DataSetObserver createDataSetObserver() {
- return new TabAdapterObserver();
- }
-
- protected TabContentFactory createTabContentFactory() {
- return new TabViewContentFactory();
- }
-
- public void setTabWidgetOnFocusChangeListener(View.OnFocusChangeListener listener) {
- getTabWidget().setOnFocusChangeListener(listener);
- }
-
- @Override
- public void onFinishInflate() {
- setup();
- // TODO: The height of the TabView is the height of the tallest
- // tab that has been displayed, because TabHost uses a FrameLayout which contains
- // all the viewed tab contents, and makes the hidden ones INVISIBLE, not GONE.
- // This is a workaround that sets the visibility to GONE for all the invisible
- // tab content views.
- // This should be fixed in TabHost instead.
- setOnTabChangedListener(new OnTabChangeListener() {
- public void onTabChanged(String tag) {
- FrameLayout contentFrame = getTabContentView();
- View currentView = getCurrentView();
- int count = contentFrame.getChildCount();
- for (int i = 0; i < count; i++) {
- View child = contentFrame.getChildAt(i);
- if (child != currentView) {
- child.setVisibility(View.GONE);
- }
- }
- }
- });
- }
-
- public int getTabHeight() {
- View tabWidget = getTabWidget();
- int height = tabWidget.getHeight();
- if (height == 0) {
- // Not measured yet, hack around it
- tabWidget.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
- height = tabWidget.getMeasuredHeight();
- }
- return height;
- }
-
- protected class TabAdapterObserver extends DataSetObserver {
- @Override
- public void onChanged() {
- onDataSetChanged();
- }
- }
-
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- //if (DBG) Log.d(TAG, "onInterceptTouchEvent(" + event + ")");
- // TODO: this should be in onTouchEvent(), but that doesn't get called.
- // TabHost weirdness?
- if (mGestureDetector.onTouchEvent(event)) {
- return true;
- }
- return super.onInterceptTouchEvent(event);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (DBG) Log.d(TAG, "onTouchEvent(" + event + ")");
- return super.onTouchEvent(event);
- }
-
- /**
- * Called when the tab set changes.
- */
- protected void onDataSetChanged() {
- if (DBG) Log.d(TAG, "onDataSetChanged() " + mAdapter);
-
- clearAllTabs();
-
- if (mAdapter == null) {
- return;
- }
-
- if (mTabContentFactory == null) {
- mTabContentFactory = createTabContentFactory();
- }
- ViewGroup tabWidget = getTabWidget();
- int count = mAdapter.getTabCount();
- for (int i = 0; i < count; i++) {
- View indicator = mAdapter.getTabHandleView(i, tabWidget);
- String tag = mAdapter.getTag(i);
- addTab(newTabSpec(tag).setIndicator(indicator).setContent(mTabContentFactory));
- }
- }
-
- private int getTabCount() {
- return mAdapter == null ? 0 : mAdapter.getTabCount();
- }
-
- protected View createTabContentView(int position) {
- return mAdapter.getTabContentView(position, getTabContentView());
- }
-
- private class TabViewContentFactory implements TabContentFactory {
- public View createTabContent(String tag) {
- if (DBG) Log.d(TAG, "createTabContent(" + tag + ")");
- int position = mAdapter.getTabPosition(tag);
- return createTabContentView(position);
- }
- }
-
- private boolean toPreviousTab() {
- int newTab = getCurrentTab() - 1;
- if (newTab >= 0) {
- setCurrentTab(newTab);
- return true;
- } else {
- return false;
- }
- }
-
- private boolean toNextTab() {
- int newTab = getCurrentTab() + 1;
- if (newTab < getTabCount()) {
- setCurrentTab(newTab);
- return true;
- } else {
- return false;
- }
- }
-
- private class GestureListener extends GestureDetector.SimpleOnGestureListener {
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- if (DBG) Log.d(TAG, "onFling(" + velocityX + "," + velocityY + ")");
- float absX = Math.abs(velocityX);
- float absY = Math.abs(velocityY);
- // TODO(bryanmawhinney): Tune these thresholds
- if (absX > 700 && absY < 300) {
- if (velocityX > 0) {
- toPreviousTab();
- } else {
- toNextTab();
- }
- }
- return true;
- }
- }
-}
diff --git a/src/com/android/quicksearchbox/ui/Util.java b/src/com/android/quicksearchbox/ui/Util.java
new file mode 100644
index 0000000..c59a7b0
--- /dev/null
+++ b/src/com/android/quicksearchbox/ui/Util.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quicksearchbox.ui;
+
+import android.graphics.Rect;
+import android.view.View;
+
+/**
+ * UI utilities.
+ */
+public class Util {
+
+ public static Rect getOnScreenRect(View view) {
+ int[] location = new int[2];
+ view.getLocationOnScreen(location);
+ Rect rect = new Rect();
+ rect.left = location[0];
+ rect.top = location[1];
+ rect.right = rect.left + view.getWidth();
+ rect.bottom = rect.top + view.getHeight();
+ return rect;
+ }
+
+}