Refactoring to make QSB more modular
Change-Id: I3bd5444bdcf4ac62a921c8c921306cc17aa440dc
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 049de81..22fc1f3 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -37,7 +37,7 @@
<application android:label="@string/app_name"
android:icon="@drawable/search_app_icon"
- android:name=".QsbApplication">
+ android:name=".QsbApplicationWrapper">
<activity android:name=".SearchActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c90ee5d..5a4f1fd 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -88,6 +88,9 @@
Home/res/values/strings.xml -->
<string name="google_search_hint">Google Search</string>
+ <!-- Search settings description for the Google search source. -->
+ <string name="google_search_description">Google search suggestions</string>
+
<!-- Settings category title for 'Google search settings' settings activity -->
<string name="google_search_settings">Google search</string>
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
index 901f069..012fe51 100644
--- a/res/xml/preferences.xml
+++ b/res/xml/preferences.xml
@@ -43,6 +43,7 @@
</PreferenceCategory>
<PreferenceCategory
+ android:key="voice_search_settings_category"
android:title="@string/voice_search_category_title">
<CheckBoxPreference
diff --git a/src/com/android/quicksearchbox/AbstractSource.java b/src/com/android/quicksearchbox/AbstractSource.java
new file mode 100644
index 0000000..7197852
--- /dev/null
+++ b/src/com/android/quicksearchbox/AbstractSource.java
@@ -0,0 +1,113 @@
+/*
+ * 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.quicksearchbox;
+
+import android.app.SearchManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * Abstract suggestion source implementation.
+ */
+public abstract class AbstractSource implements Source {
+
+ private static final String TAG = "QSB.AbstractSource";
+
+ private final Context mContext;
+
+ private IconLoader mIconLoader;
+
+ public AbstractSource(Context context) {
+ mContext = context;
+ }
+
+ protected Context getContext() {
+ return mContext;
+ }
+
+ protected IconLoader getIconLoader() {
+ if (mIconLoader == null) {
+ String iconPackage = getIconPackage();
+ mIconLoader = new CachingIconLoader(new PackageIconLoader(mContext, iconPackage));
+ }
+ return mIconLoader;
+ }
+
+ protected abstract String getIconPackage();
+
+ public Drawable getIcon(String drawableId) {
+ return getIconLoader().getIcon(drawableId);
+ }
+
+ public Uri getIconUri(String drawableId) {
+ return getIconLoader().getIconUri(drawableId);
+ }
+
+ public Intent createSearchIntent(String query, Bundle appData) {
+ return createSourceSearchIntent(getIntentComponent(), query, appData);
+ }
+
+ public static Intent createSourceSearchIntent(ComponentName activity, String query,
+ Bundle appData) {
+ if (activity == null) {
+ Log.w(TAG, "Tried to create search intent with no target activity");
+ return null;
+ }
+ Intent intent = new Intent(Intent.ACTION_SEARCH);
+ intent.setComponent(activity);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // We need CLEAR_TOP to avoid reusing an old task that has other activities
+ // on top of the one we want.
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.putExtra(SearchManager.USER_QUERY, query);
+ intent.putExtra(SearchManager.QUERY, query);
+ if (appData != null) {
+ intent.putExtra(SearchManager.APP_DATA, appData);
+ }
+ return intent;
+ }
+
+ protected Intent createVoiceWebSearchIntent(Bundle appData) {
+ return QsbApplication.get(mContext).getVoiceSearch()
+ .createVoiceWebSearchIntent(appData);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o != null && o.getClass().equals(this.getClass())) {
+ AbstractSource s = (AbstractSource) o;
+ return s.getName().equals(getName());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return getName().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "Source{name=" + getName() + "}";
+ }
+
+}
diff --git a/src/com/android/quicksearchbox/AppsCorpus.java b/src/com/android/quicksearchbox/AppsCorpus.java
index ed35825..ca41c7c 100644
--- a/src/com/android/quicksearchbox/AppsCorpus.java
+++ b/src/com/android/quicksearchbox/AppsCorpus.java
@@ -93,7 +93,7 @@
private Intent createAppSearchIntent(String query, Bundle appData) {
ComponentName name = getComponentName(getContext(), R.string.apps_search_activity);
if (name == null) return null;
- Intent intent = SearchableSource.createSourceSearchIntent(name, query, appData);
+ Intent intent = AbstractSource.createSourceSearchIntent(name, query, appData);
if (intent == null) return null;
ActivityInfo ai = intent.resolveActivityInfo(getContext().getPackageManager(), 0);
if (ai != null) {
diff --git a/src/com/android/quicksearchbox/Config.java b/src/com/android/quicksearchbox/Config.java
index 6e5b5df..987ba9f 100644
--- a/src/com/android/quicksearchbox/Config.java
+++ b/src/com/android/quicksearchbox/Config.java
@@ -214,4 +214,7 @@
return PUBLISH_RESULT_DELAY_MILLIS;
}
+ public boolean allowVoiceSearchHints() {
+ return false;
+ }
}
diff --git a/src/com/android/quicksearchbox/CorporaUpdateReceiver.java b/src/com/android/quicksearchbox/CorporaUpdateReceiver.java
index 11163c7..20ac202 100644
--- a/src/com/android/quicksearchbox/CorporaUpdateReceiver.java
+++ b/src/com/android/quicksearchbox/CorporaUpdateReceiver.java
@@ -42,11 +42,7 @@
}
private void updateCorpora(Context context) {
- getQsbApplication(context).updateCorpora();
- }
-
- private QsbApplication getQsbApplication(Context context) {
- return (QsbApplication) context.getApplicationContext();
+ QsbApplication.get(context).updateCorpora();
}
}
diff --git a/src/com/android/quicksearchbox/Corpus.java b/src/com/android/quicksearchbox/Corpus.java
index b4a0720..1affbe0 100644
--- a/src/com/android/quicksearchbox/Corpus.java
+++ b/src/com/android/quicksearchbox/Corpus.java
@@ -93,4 +93,9 @@
* Checks if this corpus should be hidden from the corpus selector.
*/
boolean isCorpusHidden();
+
+ /**
+ * Checks if this corpus is location aware.
+ */
+ boolean isLocationAware();
}
diff --git a/src/com/android/quicksearchbox/CorpusSelectionDialog.java b/src/com/android/quicksearchbox/CorpusSelectionDialog.java
index 68f2187..fcff3a9 100644
--- a/src/com/android/quicksearchbox/CorpusSelectionDialog.java
+++ b/src/com/android/quicksearchbox/CorpusSelectionDialog.java
@@ -169,7 +169,7 @@
}
private QsbApplication getQsbApplication() {
- return (QsbApplication) getContext().getApplicationContext();
+ return QsbApplication.get(getContext());
}
private CorpusRanker getCorpusRanker() {
diff --git a/src/com/android/quicksearchbox/CursorBackedSourceResult.java b/src/com/android/quicksearchbox/CursorBackedSourceResult.java
new file mode 100644
index 0000000..db847e4
--- /dev/null
+++ b/src/com/android/quicksearchbox/CursorBackedSourceResult.java
@@ -0,0 +1,52 @@
+/*
+ * 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.quicksearchbox;
+
+import android.database.Cursor;
+
+public class CursorBackedSourceResult extends CursorBackedSuggestionCursor
+ implements SourceResult {
+
+ private final Source mSource;
+
+ public CursorBackedSourceResult(Source source, String userQuery) {
+ this(source, userQuery, null);
+ }
+
+ public CursorBackedSourceResult(Source source, String userQuery, Cursor cursor) {
+ super(userQuery, cursor);
+ mSource = source;
+ }
+
+ public Source getSource() {
+ return mSource;
+ }
+
+ @Override
+ public Source getSuggestionSource() {
+ return mSource;
+ }
+
+ public boolean isSuggestionShortcut() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return mSource + "[" + getUserQuery() + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/src/com/android/quicksearchbox/EventLogLogger.java b/src/com/android/quicksearchbox/EventLogLogger.java
index cb86369..4dc6dd7 100644
--- a/src/com/android/quicksearchbox/EventLogLogger.java
+++ b/src/com/android/quicksearchbox/EventLogLogger.java
@@ -17,8 +17,6 @@
package com.android.quicksearchbox;
import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
import android.util.EventLog;
import java.util.Collection;
@@ -38,22 +36,12 @@
private final String mPackageName;
- private final int mVersionCode;
-
private final Random mRandom;
public EventLogLogger(Context context, Config config) {
mContext = context;
mConfig = config;
- mPackageName= mContext.getPackageName();
- try {
- PackageInfo pkgInfo = mContext.getPackageManager().getPackageInfo(mPackageName, 0);
- mVersionCode = pkgInfo.versionCode;
- } catch (PackageManager.NameNotFoundException ex) {
- // The current package should always exist, how else could we
- // run code from it?
- throw new RuntimeException(ex);
- }
+ mPackageName = mContext.getPackageName();
mRandom = new Random();
}
@@ -62,7 +50,7 @@
}
protected int getVersionCode() {
- return mVersionCode;
+ return QsbApplication.get(getContext()).getVersionCode();
}
protected Config getConfig() {
@@ -75,7 +63,7 @@
String startMethod = intentSource;
String currentCorpus = getCorpusLogName(corpus);
String enabledCorpora = getCorpusLogNames(orderedCorpora);
- EventLogTags.writeQsbStart(mPackageName, mVersionCode, startMethod,
+ EventLogTags.writeQsbStart(mPackageName, getVersionCode(), startMethod,
latency, currentCorpus, enabledCorpora);
}
diff --git a/src/com/android/quicksearchbox/MultiSourceCorpus.java b/src/com/android/quicksearchbox/MultiSourceCorpus.java
index a3af111..8573363 100644
--- a/src/com/android/quicksearchbox/MultiSourceCorpus.java
+++ b/src/com/android/quicksearchbox/MultiSourceCorpus.java
@@ -40,7 +40,7 @@
private int mQueryThreshold;
private boolean mQueryAfterZeroResults;
private boolean mVoiceSearchEnabled;
-
+ private boolean mIsLocationAware;
public MultiSourceCorpus(Context context, Config config,
Executor executor, Source... sources) {
@@ -96,14 +96,17 @@
return sources;
}
- private void calculateSourceProperties() {
+ private void updateSourceProperties() {
+ if (mSourcePropertiesValid) return;
mQueryThreshold = Integer.MAX_VALUE;
mQueryAfterZeroResults = false;
mVoiceSearchEnabled = false;
+ mIsLocationAware = false;
for (Source s : getSources()) {
mQueryThreshold = Math.min(mQueryThreshold, s.getQueryThreshold());
mQueryAfterZeroResults |= s.queryAfterZeroResults();
mVoiceSearchEnabled |= s.voiceSearchEnabled();
+ mIsLocationAware |= s.isLocationAware();
}
if (mQueryThreshold == Integer.MAX_VALUE) {
mQueryThreshold = 0;
@@ -112,26 +115,25 @@
}
public int getQueryThreshold() {
- if (!mSourcePropertiesValid) {
- calculateSourceProperties();
- }
+ updateSourceProperties();
return mQueryThreshold;
}
public boolean queryAfterZeroResults() {
- if (!mSourcePropertiesValid) {
- calculateSourceProperties();
- }
+ updateSourceProperties();
return mQueryAfterZeroResults;
}
public boolean voiceSearchEnabled() {
- if (!mSourcePropertiesValid) {
- calculateSourceProperties();
- }
+ updateSourceProperties();
return mVoiceSearchEnabled;
}
+ public boolean isLocationAware() {
+ updateSourceProperties();
+ return mIsLocationAware;
+ }
+
public CorpusResult getSuggestions(String query, int queryLimit, boolean onlyCorpus) {
LatencyTracker latencyTracker = new LatencyTracker();
List<Source> sources = getSourcesToQuery(query, onlyCorpus);
diff --git a/tests/src/com/android/quicksearchbox/MockLogger.java b/src/com/android/quicksearchbox/NoLogger.java
similarity index 91%
rename from tests/src/com/android/quicksearchbox/MockLogger.java
rename to src/com/android/quicksearchbox/NoLogger.java
index 38dbf23..32d6a06 100644
--- a/tests/src/com/android/quicksearchbox/MockLogger.java
+++ b/src/com/android/quicksearchbox/NoLogger.java
@@ -20,11 +20,11 @@
import java.util.List;
/**
- * Mock {@link Logger} implementation.
+ * Dummy {@link Logger} implementation.
*/
-public class MockLogger implements Logger {
+public class NoLogger implements Logger {
- public MockLogger() {
+ public NoLogger() {
}
public void logStart(int latency, String intentSource, Corpus corpus,
diff --git a/src/com/android/quicksearchbox/QsbApplication.java b/src/com/android/quicksearchbox/QsbApplication.java
index f3ffc63..e95c094 100644
--- a/src/com/android/quicksearchbox/QsbApplication.java
+++ b/src/com/android/quicksearchbox/QsbApplication.java
@@ -16,6 +16,8 @@
package com.android.quicksearchbox;
+import com.android.quicksearchbox.google.GoogleClient;
+import com.android.quicksearchbox.google.GoogleSuggestClient;
import com.android.quicksearchbox.ui.CorpusViewFactory;
import com.android.quicksearchbox.ui.CorpusViewInflater;
import com.android.quicksearchbox.ui.DelayingSuggestionsAdapter;
@@ -29,7 +31,10 @@
import com.android.quicksearchbox.util.SingleThreadNamedTaskExecutor;
import com.google.common.util.concurrent.NamingThreadFactory;
-import android.app.Application;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
@@ -38,8 +43,11 @@
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
-public class QsbApplication extends Application {
+public class QsbApplication {
+ private final Context mContext;
+
+ private int mVersionCode;
private Handler mUiThreadHandler;
private Config mConfig;
private Corpora mCorpora;
@@ -51,12 +59,39 @@
private SuggestionsProvider mSuggestionsProvider;
private SuggestionViewFactory mSuggestionViewFactory;
private CorpusViewFactory mCorpusViewFactory;
+ private GoogleClient mGoogleClient;
+ private VoiceSearch mVoiceSearch;
private Logger mLogger;
- @Override
- public void onTerminate() {
- close();
- super.onTerminate();
+ public QsbApplication(Context context) {
+ mContext = context;
+ }
+
+ public static boolean isFroyoOrLater() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;
+ }
+
+ public static QsbApplication get(Context context) {
+ return ((QsbApplicationWrapper) context.getApplicationContext()).getApp();
+ }
+
+ protected Context getContext() {
+ return mContext;
+ }
+
+ public int getVersionCode() {
+ if (mVersionCode == 0) {
+ try {
+ PackageManager pm = getContext().getPackageManager();
+ PackageInfo pkgInfo = pm.getPackageInfo(getContext().getPackageName(), 0);
+ mVersionCode = pkgInfo.versionCode;
+ } catch (PackageManager.NameNotFoundException ex) {
+ // The current package should always exist, how else could we
+ // run code from it?
+ throw new RuntimeException(ex);
+ }
+ }
+ return mVersionCode;
}
protected void checkThread() {
@@ -109,7 +144,7 @@
}
protected Config createConfig() {
- return new Config(this);
+ return new Config(getContext());
}
/**
@@ -125,7 +160,7 @@
}
protected Corpora createCorpora() {
- SearchableCorpora corpora = new SearchableCorpora(this, createSources(),
+ SearchableCorpora corpora = new SearchableCorpora(getContext(), createSources(),
createCorpusFactory());
corpora.update();
return corpora;
@@ -143,12 +178,12 @@
}
protected Sources createSources() {
- return new SearchableSources(this);
+ return new SearchableSources(getContext());
}
protected CorpusFactory createCorpusFactory() {
int numWebCorpusThreads = getConfig().getNumWebCorpusThreads();
- return new SearchableCorpusFactory(this, getConfig(),
+ return new SearchableCorpusFactory(getContext(), getConfig(),
createExecutorFactory(numWebCorpusThreads));
}
@@ -193,7 +228,7 @@
ThreadFactory logThreadFactory = new NamingThreadFactory("ShortcutRepositoryWriter #%d",
new PriorityThreadFactory(Process.THREAD_PRIORITY_BACKGROUND));
Executor logExecutor = Executors.newSingleThreadExecutor(logThreadFactory);
- return ShortcutRepositoryImplLog.create(this, getConfig(), getCorpora(),
+ return ShortcutRepositoryImplLog.create(getContext(), getConfig(), getCorpora(),
getShortcutRefresher(), getMainThreadHandler(), logExecutor);
}
@@ -227,7 +262,6 @@
}
protected NamedTaskExecutor createSourceTaskExecutor() {
- Config config = getConfig();
ThreadFactory queryThreadFactory = getQueryThreadFactory();
return new PerNameExecutor(SingleThreadNamedTaskExecutor.factory(queryThreadFactory));
}
@@ -290,7 +324,7 @@
}
protected SuggestionViewFactory createSuggestionViewFactory() {
- return new SuggestionViewInflater(this);
+ return new SuggestionViewInflater(getContext());
}
/**
@@ -306,7 +340,7 @@
}
protected CorpusViewFactory createCorpusViewFactory() {
- return new CorpusViewInflater(this);
+ return new CorpusViewInflater(getContext());
}
/**
@@ -314,13 +348,43 @@
* May only be called from the main thread.
*/
public SuggestionsAdapter createSuggestionsAdapter() {
- Config config = getConfig();
SuggestionViewFactory viewFactory = getSuggestionViewFactory();
DelayingSuggestionsAdapter adapter = new DelayingSuggestionsAdapter(viewFactory);
return adapter;
}
/**
+ * Gets the Google client.
+ * May only be called from the main thread.
+ */
+ public GoogleClient getGoogleClient() {
+ checkThread();
+ if (mGoogleClient == null) {
+ mGoogleClient = createGoogleClient();
+ }
+ return mGoogleClient;
+ }
+
+ protected GoogleClient createGoogleClient() {
+ return new GoogleSuggestClient(getContext());
+ }
+
+ /**
+ * Gets Voice Search utilities.
+ */
+ public VoiceSearch getVoiceSearch() {
+ checkThread();
+ if (mVoiceSearch == null) {
+ mVoiceSearch = createVoiceSearch();
+ }
+ return mVoiceSearch;
+ }
+
+ protected VoiceSearch createVoiceSearch() {
+ return new VoiceSearch(getContext());
+ }
+
+ /**
* Gets the event logger.
* May only be called from the main thread.
*/
@@ -333,6 +397,6 @@
}
protected Logger createLogger() {
- return new EventLogLogger(this, getConfig());
+ return new EventLogLogger(getContext(), getConfig());
}
}
diff --git a/src/com/android/quicksearchbox/QsbApplicationWrapper.java b/src/com/android/quicksearchbox/QsbApplicationWrapper.java
new file mode 100644
index 0000000..7329cdf
--- /dev/null
+++ b/src/com/android/quicksearchbox/QsbApplicationWrapper.java
@@ -0,0 +1,46 @@
+/*
+ * 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.quicksearchbox;
+
+import android.app.Application;
+
+public class QsbApplicationWrapper extends Application {
+
+ private QsbApplication mApp;
+
+ @Override
+ public void onTerminate() {
+ synchronized (this) {
+ if (mApp != null) {
+ mApp.close();
+ }
+ }
+ super.onTerminate();
+ }
+
+ public synchronized QsbApplication getApp() {
+ if (mApp == null) {
+ mApp = createQsbApplication();
+ }
+ return mApp;
+ }
+
+ protected QsbApplication createQsbApplication() {
+ return new QsbApplication(this);
+ }
+
+}
diff --git a/src/com/android/quicksearchbox/QueryTask.java b/src/com/android/quicksearchbox/QueryTask.java
index 2b8c8f4..1d55522 100644
--- a/src/com/android/quicksearchbox/QueryTask.java
+++ b/src/com/android/quicksearchbox/QueryTask.java
@@ -22,8 +22,6 @@
import android.os.Handler;
-import java.util.Iterator;
-
/**
* A task that gets suggestions from a corpus.
*/
diff --git a/src/com/android/quicksearchbox/SearchActivity.java b/src/com/android/quicksearchbox/SearchActivity.java
index 318691b..7919b96 100644
--- a/src/com/android/quicksearchbox/SearchActivity.java
+++ b/src/com/android/quicksearchbox/SearchActivity.java
@@ -99,8 +99,6 @@
protected ImageButton mVoiceSearchButton;
protected ImageButton mCorpusIndicator;
- private VoiceSearch mVoiceSearch;
-
private Corpus mCorpus;
private Bundle mAppSearchData;
private boolean mUpdateSuggestions;
@@ -140,8 +138,6 @@
mVoiceSearchButton = (ImageButton) findViewById(R.id.search_voice_btn);
mCorpusIndicator = (ImageButton) findViewById(R.id.corpus_indicator);
- mVoiceSearch = new VoiceSearch(this);
-
mQueryTextView.addTextChangedListener(new SearchTextWatcher());
mQueryTextView.setOnKeyListener(new QueryTextViewKeyListener());
mQueryTextView.setOnFocusChangeListener(new QueryTextViewFocusListener());
@@ -295,7 +291,7 @@
}
private QsbApplication getQsbApplication() {
- return (QsbApplication) getApplication();
+ return QsbApplication.get(this);
}
private Config getConfig() {
@@ -318,6 +314,10 @@
return getQsbApplication().getCorpusViewFactory();
}
+ private VoiceSearch getVoiceSearch() {
+ return QsbApplication.get(this).getVoiceSearch();
+ }
+
private Logger getLogger() {
return getQsbApplication().getLogger();
}
@@ -423,7 +423,7 @@
}
protected void updateVoiceSearchButton(boolean queryEmpty) {
- if (queryEmpty && mVoiceSearch.shouldShowVoiceSearch(mCorpus)) {
+ if (queryEmpty && getVoiceSearch().shouldShowVoiceSearch(mCorpus)) {
mVoiceSearchButton.setVisibility(View.VISIBLE);
mQueryTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
} else {
diff --git a/src/com/android/quicksearchbox/SearchSettings.java b/src/com/android/quicksearchbox/SearchSettings.java
index 80fe835..f91964a 100644
--- a/src/com/android/quicksearchbox/SearchSettings.java
+++ b/src/com/android/quicksearchbox/SearchSettings.java
@@ -59,6 +59,7 @@
private static final String CLEAR_SHORTCUTS_PREF = "clear_shortcuts";
private static final String SEARCH_ENGINE_SETTINGS_PREF = "search_engine_settings";
private static final String SEARCH_CORPORA_PREF = "search_corpora";
+ private static final String VOICE_SEARCH_CATEGORY = "voice_search_settings_category";
// Prefix of per-corpus enable preference
private static final String CORPUS_ENABLED_PREF_PREFIX = "enable_corpus_";
@@ -90,7 +91,14 @@
corporaPreference.setIntent(getSearchableItemsIntent(this));
mClearShortcutsPreference.setOnPreferenceClickListener(this);
- mVoiceSearchHintsPreference.setOnPreferenceClickListener(this);
+
+ if (getConfig().allowVoiceSearchHints()) {
+ mVoiceSearchHintsPreference.setOnPreferenceClickListener(this);
+ } else {
+ preferenceScreen.removePreference(
+ preferenceScreen.findPreference(VOICE_SEARCH_CATEGORY));
+ mVoiceSearchHintsPreference = null;
+ }
updateClearShortcutsPreference();
populateSearchEnginePreference();
@@ -125,12 +133,12 @@
return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
}
- private QsbApplication getQsbApplication() {
- return (QsbApplication) getApplication();
+ private ShortcutRepository getShortcuts() {
+ return QsbApplication.get(this).getShortcutRepository();
}
- private ShortcutRepository getShortcuts() {
- return getQsbApplication().getShortcutRepository();
+ private Config getConfig() {
+ return QsbApplication.get(this).getConfig();
}
/**
diff --git a/src/com/android/quicksearchbox/SearchWidgetConfigActivity.java b/src/com/android/quicksearchbox/SearchWidgetConfigActivity.java
index b07b21f..935cda3 100644
--- a/src/com/android/quicksearchbox/SearchWidgetConfigActivity.java
+++ b/src/com/android/quicksearchbox/SearchWidgetConfigActivity.java
@@ -54,6 +54,12 @@
if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
finish();
}
+
+ // If there is only All, or only All and one other corpus, there is no
+ // point in asking the user to select a corpus.
+ if (getCorpusRanker().getRankedCorpora().size() <= 1) {
+ selectCorpus(null);
+ }
}
@Override
@@ -106,16 +112,12 @@
return prefs.getString(getCorpusPrefKey(appWidgetId), null);
}
- private QsbApplication getQsbApplication() {
- return (QsbApplication) getApplication();
- }
-
private CorpusRanker getCorpusRanker() {
- return getQsbApplication().getCorpusRanker();
+ return QsbApplication.get(this).getCorpusRanker();
}
private CorpusViewFactory getViewFactory() {
- return getQsbApplication().getCorpusViewFactory();
+ return QsbApplication.get(this).getCorpusViewFactory();
}
private class SourceClickListener implements AdapterView.OnItemClickListener {
diff --git a/src/com/android/quicksearchbox/SearchWidgetProvider.java b/src/com/android/quicksearchbox/SearchWidgetProvider.java
index 9a0e62c..ca45aac 100644
--- a/src/com/android/quicksearchbox/SearchWidgetProvider.java
+++ b/src/com/android/quicksearchbox/SearchWidgetProvider.java
@@ -32,6 +32,7 @@
import android.content.SharedPreferences;
import android.graphics.Typeface;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
import android.speech.RecognizerIntent;
@@ -186,7 +187,7 @@
private static Intent getVoiceSearchIntent(Context context, Corpus corpus,
Bundle widgetAppData) {
- VoiceSearch voiceSearch = new VoiceSearch(context);
+ VoiceSearch voiceSearch = QsbApplication.get(context).getVoiceSearch();
if (!voiceSearch.shouldShowVoiceSearch(corpus)) return null;
if (corpus == null) {
return voiceSearch.createVoiceWebSearchIntent(widgetAppData);
@@ -224,13 +225,18 @@
return spannedHint;
}
+ private static boolean areVoiceSearchHintsEnabled(Context context) {
+ return getConfig(context).allowVoiceSearchHints()
+ && SearchSettings.areVoiceSearchHintsEnabled(context);
+ }
+
public static void scheduleVoiceSearchHintUpdates(Context context, boolean enabled) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(ACTION_NEXT_VOICE_SEARCH_HINT);
intent.setComponent(myComponentName(context));
PendingIntent updateHint = PendingIntent.getBroadcast(context, 0, intent, 0);
alarmManager.cancel(updateHint);
- if (enabled && SearchSettings.areVoiceSearchHintsEnabled(context)) {
+ if (enabled && areVoiceSearchHintsEnabled(context)) {
// Do one update immediately, and then at VOICE_SEARCH_HINT_UPDATE_INTERVAL intervals
getHintsFromVoiceSearch(context);
long period = VOICE_SEARCH_HINT_UPDATE_INTERVAL;
@@ -243,7 +249,7 @@
* Requests an asynchronous update of the voice search hints.
*/
private static void getHintsFromVoiceSearch(Context context) {
- if (!SearchSettings.areVoiceSearchHintsEnabled(context)) return;
+ if (!areVoiceSearchHintsEnabled(context)) return;
Intent intent = new Intent(RecognizerIntent.ACTION_GET_LANGUAGE_DETAILS);
intent.putExtra(Recognition.EXTRA_HINT_CONTEXT, Recognition.HINT_CONTEXT_LAUNCHER);
if (DBG) Log.d(TAG, "Broadcasting " + intent);
@@ -290,16 +296,16 @@
return i;
}
- private static QsbApplication getQsbApplication(Context context) {
- return (QsbApplication) context.getApplicationContext();
+ private static Config getConfig(Context context) {
+ return QsbApplication.get(context).getConfig();
}
private static Corpora getCorpora(Context context) {
- return getQsbApplication(context).getCorpora();
+ return QsbApplication.get(context).getCorpora();
}
private static CorpusViewFactory getCorpusViewFactory(Context context) {
- return getQsbApplication(context).getCorpusViewFactory();
+ return QsbApplication.get(context).getCorpusViewFactory();
}
private static class SearchWidgetState {
@@ -356,13 +362,19 @@
public void updateWidget(Context context, AppWidgetManager appWidgetManager) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.search_widget);
// Corpus indicator
- views.setImageViewUri(R.id.corpus_indicator, mCorpusIconUri);
+ // Before Froyo, android.resource URI could not be used in ImageViews.
+ if (QsbApplication.isFroyoOrLater()) {
+ views.setImageViewUri(R.id.corpus_indicator, mCorpusIconUri);
+ }
setOnClickActivityIntent(context, views, R.id.corpus_indicator,
mCorpusIndicatorIntent);
// Query TextView
views.setCharSequence(R.id.search_widget_text, "setHint", mQueryTextViewHint);
- views.setInt(R.id.search_widget_text, "setBackgroundResource",
- mQueryTextViewBackgroundResource);
+ // setBackgroundResource did not have @RemotableViewMethod before Froyo
+ if (QsbApplication.isFroyoOrLater()) {
+ views.setInt(R.id.search_widget_text, "setBackgroundResource",
+ mQueryTextViewBackgroundResource);
+ }
setOnClickActivityIntent(context, views, R.id.search_widget_text,
mQueryTextViewIntent);
// Voice Search button
diff --git a/src/com/android/quicksearchbox/SearchableItemsSettings.java b/src/com/android/quicksearchbox/SearchableItemsSettings.java
index a279593..2547f8b 100644
--- a/src/com/android/quicksearchbox/SearchableItemsSettings.java
+++ b/src/com/android/quicksearchbox/SearchableItemsSettings.java
@@ -52,12 +52,8 @@
populateSourcePreference();
}
- private QsbApplication getQsbApplication() {
- return (QsbApplication) getApplication();
- }
-
private Corpora getCorpora() {
- return getQsbApplication().getCorpora();
+ return QsbApplication.get(this).getCorpora();
}
/**
diff --git a/src/com/android/quicksearchbox/SearchableSource.java b/src/com/android/quicksearchbox/SearchableSource.java
index 2e71c49..5ec1282 100644
--- a/src/com/android/quicksearchbox/SearchableSource.java
+++ b/src/com/android/quicksearchbox/SearchableSource.java
@@ -42,9 +42,8 @@
/**
* Represents a single suggestion source, e.g. Contacts.
- *
*/
-public class SearchableSource implements Source {
+public class SearchableSource extends AbstractSource {
private static final boolean DBG = false;
private static final String TAG = "QSB.SearchableSource";
@@ -53,8 +52,6 @@
// The extra key used in an intent to the speech recognizer for in-app voice search.
private static final String EXTRA_CALLING_PACKAGE = "calling_package";
- private final Context mContext;
-
private final SearchableInfo mSearchable;
private final String mName;
@@ -69,12 +66,10 @@
// Cached icon for the activity
private Drawable.ConstantState mSourceIcon = null;
- private IconLoader mIconLoader;
-
public SearchableSource(Context context, SearchableInfo searchable)
throws NameNotFoundException {
+ super(context);
ComponentName componentName = searchable.getSearchActivity();
- mContext = context;
mSearchable = searchable;
mName = componentName.flattenToShortString();
PackageManager pm = context.getPackageManager();
@@ -83,10 +78,6 @@
mVersionCode = pkgInfo.versionCode;
}
- protected Context getContext() {
- return mContext;
- }
-
protected SearchableInfo getSearchableInfo() {
return mSearchable;
}
@@ -122,7 +113,7 @@
* TODO: Shouldn't this be a PackageManager / Context / ContentResolver method?
*/
private boolean canRead(Uri uri) {
- ProviderInfo provider = mContext.getPackageManager().resolveContentProvider(
+ ProviderInfo provider = getContext().getPackageManager().resolveContentProvider(
uri.getAuthority(), 0);
if (provider == null) {
Log.w(TAG, getName() + " has bad suggestion authority " + uri.getAuthority());
@@ -135,7 +126,7 @@
}
int pid = android.os.Process.myPid();
int uid = android.os.Process.myUid();
- if (mContext.checkPermission(readPermission, pid, uid)
+ if (getContext().checkPermission(readPermission, pid, uid)
== PackageManager.PERMISSION_GRANTED) {
// We have permission to read everything in the content provider
return true;
@@ -151,7 +142,7 @@
String pathReadPermission = perm.getReadPermission();
if (pathReadPermission != null
&& perm.match(path)
- && mContext.checkPermission(pathReadPermission, pid, uid)
+ && getContext().checkPermission(pathReadPermission, pid, uid)
== PackageManager.PERMISSION_GRANTED) {
// We have the path permission
return true;
@@ -161,19 +152,6 @@
return false;
}
- private IconLoader getIconLoader() {
- if (mIconLoader == null) {
- // Get icons from the package containing the suggestion provider, if any
- String iconPackage = mSearchable.getSuggestPackage();
- if (iconPackage == null) {
- // Fall back to the package containing the searchable activity
- iconPackage = mSearchable.getSearchActivity().getPackageName();
- }
- mIconLoader = new CachingIconLoader(new PackageIconLoader(mContext, iconPackage));
- }
- return mIconLoader;
- }
-
public ComponentName getIntentComponent() {
return mSearchable.getSearchActivity();
}
@@ -186,18 +164,22 @@
return mName;
}
- public Drawable getIcon(String drawableId) {
- return getIconLoader().getIcon(drawableId);
- }
-
- public Uri getIconUri(String drawableId) {
- return getIconLoader().getIconUri(drawableId);
+ @Override
+ protected String getIconPackage() {
+ // Get icons from the package containing the suggestion provider, if any
+ String iconPackage = mSearchable.getSuggestPackage();
+ if (iconPackage != null) {
+ return iconPackage;
+ } else {
+ // Fall back to the package containing the searchable activity
+ return mSearchable.getSearchActivity().getPackageName();
+ }
}
public CharSequence getLabel() {
if (mLabel == null) {
// Load label lazily
- mLabel = mActivityInfo.loadLabel(mContext.getPackageManager());
+ mLabel = mActivityInfo.loadLabel(getContext().getPackageManager());
}
return mLabel;
}
@@ -218,7 +200,7 @@
if (mSourceIcon == null) {
// Load icon lazily
int iconRes = getSourceIconResource();
- PackageManager pm = mContext.getPackageManager();
+ PackageManager pm = getContext().getPackageManager();
Drawable icon = pm.getDrawable(mActivityInfo.packageName, iconRes,
mActivityInfo.applicationInfo);
// Can't share Drawable instances, save constant state instead.
@@ -243,33 +225,13 @@
return mSearchable.getVoiceSearchEnabled();
}
- public Intent createSearchIntent(String query, Bundle appData) {
- return createSourceSearchIntent(getIntentComponent(), query, appData);
- }
-
- public static Intent createSourceSearchIntent(ComponentName activity, String query,
- Bundle appData) {
- if (activity == null) {
- Log.w(TAG, "Tried to create search intent with no target activity");
- return null;
- }
- Intent intent = new Intent(Intent.ACTION_SEARCH);
- intent.setComponent(activity);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- // We need CLEAR_TOP to avoid reusing an old task that has other activities
- // on top of the one we want.
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.putExtra(SearchManager.USER_QUERY, query);
- intent.putExtra(SearchManager.QUERY, query);
- if (appData != null) {
- intent.putExtra(SearchManager.APP_DATA, appData);
- }
- return intent;
+ public boolean isLocationAware() {
+ return false;
}
public Intent createVoiceSearchIntent(Bundle appData) {
if (mSearchable.getVoiceSearchLaunchWebSearch()) {
- return new VoiceSearch(mContext).createVoiceWebSearchIntent(appData);
+ return createVoiceWebSearchIntent(appData);
} else if (mSearchable.getVoiceSearchLaunchRecognizer()) {
return createVoiceAppSearchIntent(appData);
}
@@ -338,24 +300,24 @@
public SourceResult getSuggestions(String query, int queryLimit, boolean onlySource) {
try {
- Cursor cursor = getSuggestions(mContext, mSearchable, query, queryLimit);
+ Cursor cursor = getSuggestions(getContext(), mSearchable, query, queryLimit);
if (DBG) Log.d(TAG, toString() + "[" + query + "] returned.");
- return new CursorBackedSourceResult(query, cursor);
+ return new CursorBackedSourceResult(this, query, cursor);
} catch (RuntimeException ex) {
Log.e(TAG, toString() + "[" + query + "] failed", ex);
- return new CursorBackedSourceResult(query);
+ return new CursorBackedSourceResult(this, query);
}
}
public SuggestionCursor refreshShortcut(String shortcutId, String extraData) {
Cursor cursor = null;
try {
- cursor = getValidationCursor(mContext, mSearchable, shortcutId, extraData);
+ cursor = getValidationCursor(getContext(), mSearchable, shortcutId, extraData);
if (DBG) Log.d(TAG, toString() + "[" + shortcutId + "] returned.");
if (cursor != null && cursor.getCount() > 0) {
cursor.moveToFirst();
}
- return new CursorBackedSourceResult(null, cursor);
+ return new CursorBackedSourceResult(this, null, cursor);
} catch (RuntimeException ex) {
Log.e(TAG, toString() + "[" + shortcutId + "] failed", ex);
if (cursor != null) {
@@ -366,37 +328,6 @@
}
}
- private class CursorBackedSourceResult extends CursorBackedSuggestionCursor
- implements SourceResult {
-
- public CursorBackedSourceResult(String userQuery) {
- this(userQuery, null);
- }
-
- public CursorBackedSourceResult(String userQuery, Cursor cursor) {
- super(userQuery, cursor);
- }
-
- public Source getSource() {
- return SearchableSource.this;
- }
-
- @Override
- public Source getSuggestionSource() {
- return SearchableSource.this;
- }
-
- public boolean isSuggestionShortcut() {
- return false;
- }
-
- @Override
- public String toString() {
- return SearchableSource.this + "[" + getUserQuery() + "]";
- }
-
- }
-
/**
* This is a copy of {@link SearchManager#getSuggestions(SearchableInfo, String)}.
*/
@@ -484,25 +415,6 @@
return mSearchable.queryAfterZeroResults();
}
- @Override
- public boolean equals(Object o) {
- if (o != null && o.getClass().equals(this.getClass())) {
- SearchableSource s = (SearchableSource) o;
- return s.mName.equals(mName);
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return mName.hashCode();
- }
-
- @Override
- public String toString() {
- return "SearchableSource{component=" + getName() + "}";
- }
-
public String getDefaultIntentAction() {
return mSearchable.getSuggestIntentAction();
}
@@ -513,7 +425,7 @@
private CharSequence getText(int id) {
if (id == 0) return null;
- return mContext.getPackageManager().getText(mActivityInfo.packageName, id,
+ return getContext().getPackageManager().getText(mActivityInfo.packageName, id,
mActivityInfo.applicationInfo);
}
diff --git a/src/com/android/quicksearchbox/SearchableSources.java b/src/com/android/quicksearchbox/SearchableSources.java
index 649df1d..edb66e8 100644
--- a/src/com/android/quicksearchbox/SearchableSources.java
+++ b/src/com/android/quicksearchbox/SearchableSources.java
@@ -16,12 +16,12 @@
package com.android.quicksearchbox;
+import com.android.quicksearchbox.google.GoogleSource;
+
import android.app.SearchManager;
import android.app.SearchableInfo;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;
@@ -56,6 +56,14 @@
mSearchManager = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
}
+ protected Context getContext() {
+ return mContext;
+ }
+
+ protected SearchManager getSearchManager() {
+ return mSearchManager;
+ }
+
public Collection<Source> getSources() {
return mSources.values();
}
@@ -100,24 +108,8 @@
mSources.put(source.getName(), source);
}
- private Source createWebSearchSource() {
- ComponentName name = getWebSearchComponent();
- SearchableInfo webSearchable = mSearchManager.getSearchableInfo(name);
- if (webSearchable == null) {
- Log.e(TAG, "Web search source " + name + " is not searchable.");
- return null;
- }
- return createSearchableSource(webSearchable);
- }
-
- private ComponentName getWebSearchComponent() {
- // Looks for an activity in the current package that handles ACTION_WEB_SEARCH.
- // This indirect method is used to allow easy replacement of the web
- // search activity when extending this package.
- Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH);
- webSearchIntent.setPackage(mContext.getPackageName());
- PackageManager pm = mContext.getPackageManager();
- return webSearchIntent.resolveActivity(pm);
+ protected Source createWebSearchSource() {
+ return new GoogleSource(getContext());
}
private SearchableSource createSearchableSource(SearchableInfo searchable) {
diff --git a/src/com/android/quicksearchbox/ShortcutRepositoryImplLog.java b/src/com/android/quicksearchbox/ShortcutRepositoryImplLog.java
index 1331afb..08b8503 100644
--- a/src/com/android/quicksearchbox/ShortcutRepositoryImplLog.java
+++ b/src/com/android/quicksearchbox/ShortcutRepositoryImplLog.java
@@ -293,6 +293,7 @@
if (refreshed == null || refreshed.getCount() == 0) {
shortcut = null;
} else {
+ refreshed.moveTo(0);
shortcut = makeShortcutRow(refreshed);
}
diff --git a/src/com/android/quicksearchbox/ShortcutsProvider.java b/src/com/android/quicksearchbox/ShortcutsProvider.java
index a8bed1c..66b6730 100644
--- a/src/com/android/quicksearchbox/ShortcutsProvider.java
+++ b/src/com/android/quicksearchbox/ShortcutsProvider.java
@@ -182,7 +182,7 @@
}
private QsbApplication getQsbApplication() {
- return (QsbApplication) getContext().getApplicationContext();
+ return QsbApplication.get(getContext());
}
private ShortcutRepository getShortcutRepository() {
diff --git a/src/com/android/quicksearchbox/SingleSourceCorpus.java b/src/com/android/quicksearchbox/SingleSourceCorpus.java
index 79f215d..8f3bf34 100644
--- a/src/com/android/quicksearchbox/SingleSourceCorpus.java
+++ b/src/com/android/quicksearchbox/SingleSourceCorpus.java
@@ -97,6 +97,10 @@
return false;
}
+ public boolean isLocationAware() {
+ return mSource.isLocationAware();
+ }
+
public Collection<Source> getSources() {
return Collections.singletonList(mSource);
}
diff --git a/src/com/android/quicksearchbox/Source.java b/src/com/android/quicksearchbox/Source.java
index 2d0a434..dafd901 100644
--- a/src/com/android/quicksearchbox/Source.java
+++ b/src/com/android/quicksearchbox/Source.java
@@ -101,6 +101,8 @@
boolean voiceSearchEnabled();
+ boolean isLocationAware();
+
Intent createSearchIntent(String query, Bundle appData);
Intent createVoiceSearchIntent(Bundle appData);
diff --git a/src/com/android/quicksearchbox/VoiceSearch.java b/src/com/android/quicksearchbox/VoiceSearch.java
index acc5860..2821e34 100644
--- a/src/com/android/quicksearchbox/VoiceSearch.java
+++ b/src/com/android/quicksearchbox/VoiceSearch.java
@@ -34,6 +34,10 @@
mContext = context;
}
+ protected Context getContext() {
+ return mContext;
+ }
+
public boolean shouldShowVoiceSearch(Corpus corpus) {
if (corpus != null && !corpus.voiceSearchEnabled()) {
return false;
diff --git a/src/com/android/quicksearchbox/WebCorpus.java b/src/com/android/quicksearchbox/WebCorpus.java
index e6442ac..b55536e 100644
--- a/src/com/android/quicksearchbox/WebCorpus.java
+++ b/src/com/android/quicksearchbox/WebCorpus.java
@@ -101,7 +101,11 @@
}
public Intent createVoiceSearchIntent(Bundle appData) {
- return new VoiceSearch(getContext()).createVoiceWebSearchIntent(appData);
+ if (mWebSearchSource != null){
+ return mWebSearchSource.createVoiceSearchIntent(appData);
+ } else {
+ return null;
+ }
}
private int getCorpusIconResource() {
diff --git a/src/com/android/quicksearchbox/google/GoogleClient.java b/src/com/android/quicksearchbox/google/GoogleClient.java
new file mode 100644
index 0000000..5d578ae
--- /dev/null
+++ b/src/com/android/quicksearchbox/google/GoogleClient.java
@@ -0,0 +1,32 @@
+/*
+ * 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.quicksearchbox.google;
+
+import android.content.ComponentName;
+import android.database.Cursor;
+
+/**
+ * Interface for Google suggestion clients.
+ */
+public interface GoogleClient {
+
+ public ComponentName getIntentComponent();
+
+ public Cursor query(String query);
+
+ public Cursor refreshShortcut(String shortcutId, String oldExtraData);
+
+}
diff --git a/src/com/android/quicksearchbox/google/GoogleSettings.java b/src/com/android/quicksearchbox/google/GoogleSettings.java
index a9cc102..86b4c48 100644
--- a/src/com/android/quicksearchbox/google/GoogleSettings.java
+++ b/src/com/android/quicksearchbox/google/GoogleSettings.java
@@ -39,7 +39,6 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.google_preferences);
- PreferenceScreen preferenceScreen = getPreferenceScreen();
mShowWebSuggestionsPreference = (CheckBoxPreference)
findPreference(SHOW_WEB_SUGGESTIONS_PREF);
mShowWebSuggestionsPreference.setOnPreferenceClickListener(this);
diff --git a/src/com/android/quicksearchbox/google/GoogleSource.java b/src/com/android/quicksearchbox/google/GoogleSource.java
new file mode 100644
index 0000000..ab83174
--- /dev/null
+++ b/src/com/android/quicksearchbox/google/GoogleSource.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.quicksearchbox.google;
+
+import com.android.quicksearchbox.AbstractSource;
+import com.android.quicksearchbox.CursorBackedSourceResult;
+import com.android.quicksearchbox.IconLoader;
+import com.android.quicksearchbox.QsbApplication;
+import com.android.quicksearchbox.R;
+import com.android.quicksearchbox.SourceResult;
+import com.android.quicksearchbox.SuggestionCursor;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+
+/**
+ * Special source implementation for Google suggestions.
+ */
+public class GoogleSource extends AbstractSource {
+
+ private static final String GOOGLE_SOURCE_NAME = "google";
+
+ private final GoogleClient mClient;
+
+ public GoogleSource(Context context) {
+ super(context);
+ mClient = QsbApplication.get(context).getGoogleClient();
+ }
+
+ public boolean canRead() {
+ return true;
+ }
+
+ public Intent createVoiceSearchIntent(Bundle appData) {
+ return createVoiceWebSearchIntent(appData);
+ }
+
+ public String getDefaultIntentAction() {
+ return Intent.ACTION_WEB_SEARCH;
+ }
+
+ public String getDefaultIntentData() {
+ return null;
+ }
+
+ public CharSequence getHint() {
+ return getContext().getString(R.string.google_search_hint);
+ }
+
+ @Override
+ protected String getIconPackage() {
+ return getContext().getPackageName();
+ }
+
+ public ComponentName getIntentComponent() {
+ return mClient.getIntentComponent();
+ }
+
+ public CharSequence getLabel() {
+ return getContext().getString(R.string.google_search_label);
+ }
+
+ public String getName() {
+ return GOOGLE_SOURCE_NAME;
+ }
+
+ public int getQueryThreshold() {
+ return 0;
+ }
+
+ public CharSequence getSettingsDescription() {
+ return getContext().getString(R.string.google_search_description);
+ }
+
+ public Drawable getSourceIcon() {
+ return getContext().getResources().getDrawable(getSourceIconResource());
+ }
+
+ public Uri getSourceIconUri() {
+ return Uri.parse("android.resource://" + getContext().getPackageName()
+ + "/" + getSourceIconResource());
+ }
+
+ private int getSourceIconResource() {
+ return R.drawable.google_icon;
+ }
+
+ public SourceResult getSuggestions(String query, int queryLimit, boolean onlySource) {
+ Cursor cursor = mClient.query(query);
+ return new CursorBackedSourceResult(this, query, cursor);
+ }
+
+ public int getVersionCode() {
+ return QsbApplication.get(getContext()).getVersionCode();
+ }
+
+ public boolean queryAfterZeroResults() {
+ return true;
+ }
+
+ public SuggestionCursor refreshShortcut(String shortcutId, String extraData) {
+ Cursor cursor = mClient.refreshShortcut(shortcutId, extraData);
+ return new CursorBackedSourceResult(this, null, cursor);
+ }
+
+ public boolean voiceSearchEnabled() {
+ return true;
+ }
+
+ public boolean isWebSuggestionSource() {
+ return true;
+ }
+
+ public boolean isLocationAware() {
+ return true;
+ }
+
+}
diff --git a/src/com/android/quicksearchbox/google/GoogleSuggestClient.java b/src/com/android/quicksearchbox/google/GoogleSuggestClient.java
new file mode 100644
index 0000000..13b8e93
--- /dev/null
+++ b/src/com/android/quicksearchbox/google/GoogleSuggestClient.java
@@ -0,0 +1,279 @@
+/*
+ * 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.quicksearchbox.google;
+
+import com.android.quicksearchbox.R;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONArray;
+import org.json.JSONException;
+
+import android.app.SearchManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.database.AbstractCursor;
+import android.database.Cursor;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Locale;
+
+/**
+ * Use network-based Google Suggests to provide search suggestions.
+ */
+public class GoogleSuggestClient implements GoogleClient {
+
+ private static final boolean DBG = false;
+ private static final String LOG_TAG = "GoogleSearch";
+
+ private static final String USER_AGENT = "Android/1.0";
+ private String mSuggestUri;
+ private static final int HTTP_TIMEOUT_MS = 1000;
+
+ // TODO: this should be defined somewhere
+ private static final String HTTP_TIMEOUT = "http.connection-manager.timeout";
+
+ // Indexes into COLUMNS
+ private static final int COL_ID = 0;
+ private static final int COL_TEXT_1 = 1;
+ private static final int COL_TEXT_2 = 2;
+ private static final int COL_ICON_1 = 3;
+ private static final int COL_ICON_2 = 4;
+ private static final int COL_QUERY = 5;
+
+ /* The suggestion columns used */
+ private static final String[] COLUMNS = new String[] {
+ "_id",
+ SearchManager.SUGGEST_COLUMN_TEXT_1,
+ SearchManager.SUGGEST_COLUMN_TEXT_2,
+ SearchManager.SUGGEST_COLUMN_ICON_1,
+ SearchManager.SUGGEST_COLUMN_ICON_2,
+ SearchManager.SUGGEST_COLUMN_QUERY
+ };
+
+ private Context mContext;
+ private HttpClient mHttpClient;
+
+ public GoogleSuggestClient(Context context) {
+ mContext = context;
+ mHttpClient = new DefaultHttpClient();
+ HttpParams params = mHttpClient.getParams();
+ HttpProtocolParams.setUserAgent(params, USER_AGENT);
+ params.setLongParameter(HTTP_TIMEOUT, HTTP_TIMEOUT_MS);
+
+ // NOTE: Do not look up the resource here; Localization changes may not have completed
+ // yet (e.g. we may still be reading the SIM card).
+ mSuggestUri = null;
+ }
+
+ protected Context getContext() {
+ return mContext;
+ }
+
+ public ComponentName getIntentComponent() {
+ return new ComponentName(getContext(), GoogleSearch.class);
+ }
+
+ /**
+ * Queries for a given search term and returns a cursor containing
+ * suggestions ordered by best match.
+ */
+ public Cursor query(String query) {
+ if (TextUtils.isEmpty(query)) {
+ return null;
+ }
+ if (!isNetworkConnected()) {
+ Log.i(LOG_TAG, "Not connected to network.");
+ return null;
+ }
+ try {
+ query = URLEncoder.encode(query, "UTF-8");
+ // NOTE: This code uses resources to optionally select the search Uri, based on the
+ // MCC value from the SIM. iThe default string will most likely be fine. It is
+ // paramerterized to accept info from the Locale, the language code is the first
+ // parameter (%1$s) and the country code is the second (%2$s). This code *must*
+ // function in the same way as a similar lookup in
+ // com.android.browser.BrowserActivity#onCreate(). If you change
+ // either of these functions, change them both. (The same is true for the underlying
+ // resource strings, which are stored in mcc-specific xml files.)
+ if (mSuggestUri == null) {
+ Locale l = Locale.getDefault();
+ String language = l.getLanguage();
+ String country = l.getCountry().toLowerCase();
+ // Chinese and Portuguese have two langauge variants.
+ if ("zh".equals(language)) {
+ if ("cn".equals(country)) {
+ language = "zh-CN";
+ } else if ("tw".equals(country)) {
+ language = "zh-TW";
+ }
+ } else if ("pt".equals(language)) {
+ if ("br".equals(country)) {
+ language = "pt-BR";
+ } else if ("pt".equals(country)) {
+ language = "pt-PT";
+ }
+ }
+ mSuggestUri = getContext().getResources().getString(R.string.google_suggest_base,
+ language,
+ country)
+ + "json=true&q=";
+ }
+
+ String suggestUri = mSuggestUri + query;
+ if (DBG) Log.d(LOG_TAG, "Sending request: " + suggestUri);
+ HttpGet method = new HttpGet(suggestUri);
+ HttpResponse response = mHttpClient.execute(method);
+ if (response.getStatusLine().getStatusCode() == 200) {
+
+ /* Goto http://www.google.com/complete/search?json=true&q=foo
+ * to see what the data format looks like. It's basically a json
+ * array containing 4 other arrays. We only care about the middle
+ * 2 which contain the suggestions and their popularity.
+ */
+ JSONArray results = new JSONArray(EntityUtils.toString(response.getEntity()));
+ JSONArray suggestions = results.getJSONArray(1);
+ JSONArray popularity = results.getJSONArray(2);
+ if (DBG) Log.d(LOG_TAG, "Got " + suggestions.length() + " results");
+ return new SuggestionsCursor(suggestions, popularity);
+ } else {
+ if (DBG) Log.d(LOG_TAG, "Request failed " + response.getStatusLine());
+ }
+ } catch (UnsupportedEncodingException e) {
+ Log.w(LOG_TAG, "Error", e);
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "Error", e);
+ } catch (JSONException e) {
+ Log.w(LOG_TAG, "Error", e);
+ }
+ return null;
+ }
+
+ public Cursor refreshShortcut(String shortcutId, String oldExtraData) {
+ return null;
+ }
+
+ private boolean isNetworkConnected() {
+ NetworkInfo networkInfo = getActiveNetworkInfo();
+ return networkInfo != null && networkInfo.isConnected();
+ }
+
+ private NetworkInfo getActiveNetworkInfo() {
+ ConnectivityManager connectivity =
+ (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ if (connectivity == null) {
+ return null;
+ }
+ return connectivity.getActiveNetworkInfo();
+ }
+
+ private static class SuggestionsCursor extends AbstractCursor {
+
+ /* Contains the actual suggestions */
+ final JSONArray mSuggestions;
+
+ /* This contains the popularity of each suggestion
+ * i.e. 165,000 results. It's not related to sorting.
+ */
+ final JSONArray mPopularity;
+ public SuggestionsCursor(JSONArray suggestions, JSONArray popularity) {
+ mSuggestions = suggestions;
+ mPopularity = popularity;
+ }
+
+ @Override
+ public int getCount() {
+ return mSuggestions.length();
+ }
+
+ @Override
+ public String[] getColumnNames() {
+ return COLUMNS;
+ }
+
+ @Override
+ public String getString(int column) {
+ if (mPos == -1) return null;
+ try {
+ switch (column) {
+ case COL_ID:
+ return String.valueOf(mPos);
+ case COL_TEXT_1:
+ case COL_QUERY:
+ return mSuggestions.getString(mPos);
+ case COL_TEXT_2:
+ return mPopularity.getString(mPos);
+ case COL_ICON_1:
+ return String.valueOf(R.drawable.magnifying_glass);
+ case COL_ICON_2:
+ return null;
+ default:
+ Log.w(LOG_TAG, "Bad column: " + column);
+ return null;
+ }
+ } catch (JSONException e) {
+ Log.w(LOG_TAG, "Error parsing response: " + e);
+ return null;
+ }
+
+ }
+
+ @Override
+ public double getDouble(int column) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public float getFloat(int column) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getInt(int column) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getLong(int column) {
+ if (column == COL_ID) {
+ return mPos; // use row# as the _Id
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public short getShort(int column) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isNull(int column) {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/src/com/android/quicksearchbox/google/GoogleSuggestionProvider.java b/src/com/android/quicksearchbox/google/GoogleSuggestionProvider.java
index b461a98..f5c7e5a 100644
--- a/src/com/android/quicksearchbox/google/GoogleSuggestionProvider.java
+++ b/src/com/android/quicksearchbox/google/GoogleSuggestionProvider.java
@@ -16,80 +16,35 @@
package com.android.quicksearchbox.google;
-import com.android.quicksearchbox.R;
-
-import org.apache.http.HttpResponse;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.params.HttpParams;
-import org.apache.http.util.EntityUtils;
-import org.json.JSONArray;
-import org.json.JSONException;
+import com.android.quicksearchbox.QsbApplication;
+import com.android.quicksearchbox.google.GoogleClient;
import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
-import android.database.AbstractCursor;
+import android.content.UriMatcher;
import android.database.Cursor;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
import android.net.Uri;
-import android.net.http.AndroidHttpClient;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.util.Locale;
/**
- * Use network-based Google Suggests to provide search suggestions.
- *
- * Future: Merge live suggestions with saved recent queries
+ * A suggestion provider which provides content from Genie, a service that offers
+ * a superset of the content provided by Google Suggest.
*/
public class GoogleSuggestionProvider extends ContentProvider {
- private static final boolean DBG = false;
- private static final String LOG_TAG = "GoogleSearch";
+ // UriMatcher constants
+ private static final int SEARCH_SUGGEST = 0;
+ private static final int SEARCH_SHORTCUT = 1;
- private static final String USER_AGENT = "Android/1.0";
- private String mSuggestUri;
- private static final int HTTP_TIMEOUT_MS = 1000;
+ private UriMatcher mUriMatcher;
- // TODO: this should be defined somewhere
- private static final String HTTP_TIMEOUT = "http.connection-manager.timeout";
-
- // Indexes into COLUMNS
- private static final int COL_ID = 0;
- private static final int COL_TEXT_1 = 1;
- private static final int COL_TEXT_2 = 2;
- private static final int COL_ICON_1 = 3;
- private static final int COL_ICON_2 = 4;
- private static final int COL_QUERY = 5;
-
- /* The suggestion columns used */
- private static final String[] COLUMNS = new String[] {
- "_id",
- SearchManager.SUGGEST_COLUMN_TEXT_1,
- SearchManager.SUGGEST_COLUMN_TEXT_2,
- SearchManager.SUGGEST_COLUMN_ICON_1,
- SearchManager.SUGGEST_COLUMN_ICON_2,
- SearchManager.SUGGEST_COLUMN_QUERY
- };
-
- private HttpClient mHttpClient;
+ private GoogleClient mClient;
@Override
public boolean onCreate() {
- mHttpClient = AndroidHttpClient.newInstance(USER_AGENT, getContext());
- HttpParams params = mHttpClient.getParams();
- params.setLongParameter(HTTP_TIMEOUT, HTTP_TIMEOUT_MS);
-
- // NOTE: Do not look up the resource here; Localization changes may not have completed
- // yet (e.g. we may still be reading the SIM card).
- mSuggestUri = null;
+ mClient = QsbApplication.get(getContext()).getGoogleClient();
+ mUriMatcher = buildUriMatcher(getContext());
return true;
}
@@ -102,82 +57,23 @@
return SearchManager.SUGGEST_MIME_TYPE;
}
- /**
- * Queries for a given search term and returns a cursor containing
- * suggestions ordered by best match.
- */
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
- String query = getQuery(uri);
- if (TextUtils.isEmpty(query)) {
- return null;
- }
- if (!isNetworkConnected()) {
- Log.i(LOG_TAG, "Not connected to network.");
- return null;
- }
- try {
- query = URLEncoder.encode(query, "UTF-8");
- // NOTE: This code uses resources to optionally select the search Uri, based on the
- // MCC value from the SIM. iThe default string will most likely be fine. It is
- // paramerterized to accept info from the Locale, the language code is the first
- // parameter (%1$s) and the country code is the second (%2$s). This code *must*
- // function in the same way as a similar lookup in
- // com.android.browser.BrowserActivity#onCreate(). If you change
- // either of these functions, change them both. (The same is true for the underlying
- // resource strings, which are stored in mcc-specific xml files.)
- if (mSuggestUri == null) {
- Locale l = Locale.getDefault();
- String language = l.getLanguage();
- String country = l.getCountry().toLowerCase();
- // Chinese and Portuguese have two langauge variants.
- if ("zh".equals(language)) {
- if ("cn".equals(country)) {
- language = "zh-CN";
- } else if ("tw".equals(country)) {
- language = "zh-TW";
- }
- } else if ("pt".equals(language)) {
- if ("br".equals(country)) {
- language = "pt-BR";
- } else if ("pt".equals(country)) {
- language = "pt-PT";
- }
- }
- mSuggestUri = getContext().getResources().getString(R.string.google_suggest_base,
- language,
- country)
- + "json=true&q=";
- }
- String suggestUri = mSuggestUri + query;
- if (DBG) Log.d(LOG_TAG, "Sending request: " + suggestUri);
- HttpGet method = new HttpGet(suggestUri);
- HttpResponse response = mHttpClient.execute(method);
- if (response.getStatusLine().getStatusCode() == 200) {
+ int match = mUriMatcher.match(uri);
- /* Goto http://www.google.com/complete/search?json=true&q=foo
- * to see what the data format looks like. It's basically a json
- * array containing 4 other arrays. We only care about the middle
- * 2 which contain the suggestions and their popularity.
- */
- JSONArray results = new JSONArray(EntityUtils.toString(response.getEntity()));
- JSONArray suggestions = results.getJSONArray(1);
- JSONArray popularity = results.getJSONArray(2);
- if (DBG) Log.d(LOG_TAG, "Got " + suggestions.length() + " results");
- return new SuggestionsCursor(suggestions, popularity);
- } else {
- if (DBG) Log.d(LOG_TAG, "Request failed " + response.getStatusLine());
- }
- } catch (UnsupportedEncodingException e) {
- Log.w(LOG_TAG, "Error", e);
- } catch (IOException e) {
- Log.w(LOG_TAG, "Error", e);
- } catch (JSONException e) {
- Log.w(LOG_TAG, "Error", e);
+ if (match == SEARCH_SUGGEST) {
+ String query = getQuery(uri);
+ return mClient.query(query);
+ } else if (match == SEARCH_SHORTCUT) {
+ String query = getQuery(uri);
+ String extraData =
+ uri.getQueryParameter(SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
+ return mClient.refreshShortcut(query, extraData);
+ } else {
+ throw new IllegalArgumentException("Unknown URI " + uri);
}
- return null;
}
/**
@@ -191,105 +87,6 @@
}
}
- private boolean isNetworkConnected() {
- NetworkInfo networkInfo = getActiveNetworkInfo();
- return networkInfo != null && networkInfo.isConnected();
- }
-
- private NetworkInfo getActiveNetworkInfo() {
- ConnectivityManager connectivity =
- (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
- if (connectivity == null) {
- return null;
- }
- return connectivity.getActiveNetworkInfo();
- }
-
- private static class SuggestionsCursor extends AbstractCursor {
-
- /* Contains the actual suggestions */
- final JSONArray mSuggestions;
-
- /* This contains the popularity of each suggestion
- * i.e. 165,000 results. It's not related to sorting.
- */
- final JSONArray mPopularity;
- public SuggestionsCursor(JSONArray suggestions, JSONArray popularity) {
- mSuggestions = suggestions;
- mPopularity = popularity;
- }
-
- @Override
- public int getCount() {
- return mSuggestions.length();
- }
-
- @Override
- public String[] getColumnNames() {
- return COLUMNS;
- }
-
- @Override
- public String getString(int column) {
- if (mPos == -1) return null;
- try {
- switch (column) {
- case COL_ID:
- return String.valueOf(mPos);
- case COL_TEXT_1:
- case COL_QUERY:
- return mSuggestions.getString(mPos);
- case COL_TEXT_2:
- return mPopularity.getString(mPos);
- case COL_ICON_1:
- return String.valueOf(R.drawable.magnifying_glass);
- case COL_ICON_2:
- return null;
- default:
- Log.w(LOG_TAG, "Bad column: " + column);
- return null;
- }
- } catch (JSONException e) {
- Log.w(LOG_TAG, "Error parsing response: " + e);
- return null;
- }
-
- }
-
- @Override
- public double getDouble(int column) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public float getFloat(int column) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int getInt(int column) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public long getLong(int column) {
- if (column == COL_ID) {
- return mPos; // use row# as the _Id
- }
- throw new UnsupportedOperationException();
- }
-
- @Override
- public short getShort(int column) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean isNull(int column) {
- throw new UnsupportedOperationException();
- }
- }
-
@Override
public Uri insert(Uri uri, ContentValues values) {
throw new UnsupportedOperationException();
@@ -305,4 +102,23 @@
public int delete(Uri uri, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException();
}
+
+ private UriMatcher buildUriMatcher(Context context) {
+ String authority = getAuthority(context);
+ UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
+ matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_QUERY,
+ SEARCH_SUGGEST);
+ matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
+ SEARCH_SUGGEST);
+ matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_SHORTCUT,
+ SEARCH_SHORTCUT);
+ matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
+ SEARCH_SHORTCUT);
+ return matcher;
+ }
+
+ protected String getAuthority(Context context) {
+ return context.getPackageName() + ".google";
+ }
+
}
diff --git a/src/com/android/quicksearchbox/ui/ContactSuggestionView.java b/src/com/android/quicksearchbox/ui/ContactSuggestionView.java
index 0164c4c..6922b1d 100644
--- a/src/com/android/quicksearchbox/ui/ContactSuggestionView.java
+++ b/src/com/android/quicksearchbox/ui/ContactSuggestionView.java
@@ -16,12 +16,13 @@
package com.android.quicksearchbox.ui;
+import com.android.quicksearchbox.R;
+import com.android.quicksearchbox.SuggestionCursor;
+
import android.content.Context;
import android.net.Uri;
import android.util.AttributeSet;
import android.widget.QuickContactBadge;
-import com.android.quicksearchbox.R;
-import com.android.quicksearchbox.SuggestionCursor;
/**
* View for contacts appearing in the suggestions list.
diff --git a/src/com/android/quicksearchbox/ui/DefaultSuggestionView.java b/src/com/android/quicksearchbox/ui/DefaultSuggestionView.java
index 8c45251..a82f0e9 100644
--- a/src/com/android/quicksearchbox/ui/DefaultSuggestionView.java
+++ b/src/com/android/quicksearchbox/ui/DefaultSuggestionView.java
@@ -21,7 +21,6 @@
import com.android.quicksearchbox.SuggestionCursor;
import android.content.Context;
-import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.text.Html;
@@ -140,14 +139,15 @@
public Drawable getSuggestionDrawableIcon1(SuggestionCursor suggestion) {
Source source = suggestion.getSuggestionSource();
- String icon1Id = suggestion.getSuggestionIcon1();
- Drawable icon1 = source.getIcon(icon1Id);
+ String iconId = suggestion.getSuggestionIcon1();
+ Drawable icon1 = iconId == null ? null : source.getIcon(iconId);
return icon1 == null ? source.getSourceIcon() : icon1;
}
public Drawable getSuggestionDrawableIcon2(SuggestionCursor suggestion) {
Source source = suggestion.getSuggestionSource();
- return source.getIcon(suggestion.getSuggestionIcon2());
+ String iconId = suggestion.getSuggestionIcon2();
+ return iconId == null ? null : source.getIcon(iconId);
}
private CharSequence formatText(String str, String format) {
diff --git a/tests/partial/src/com/android/quicksearchbox/tests/partial/PartialSuggestionLauncher.java b/tests/partial/src/com/android/quicksearchbox/tests/partial/PartialSuggestionLauncher.java
index bfff996..e088676 100644
--- a/tests/partial/src/com/android/quicksearchbox/tests/partial/PartialSuggestionLauncher.java
+++ b/tests/partial/src/com/android/quicksearchbox/tests/partial/PartialSuggestionLauncher.java
@@ -16,9 +16,7 @@
package com.android.quicksearchbox.tests.partial;
-import com.android.quicksearchbox.tests.partial.R;
import android.app.Activity;
-import android.content.Intent;
import android.os.Bundle;
public class PartialSuggestionLauncher extends Activity {
diff --git a/tests/src/com/android/quicksearchbox/MockCorpus.java b/tests/src/com/android/quicksearchbox/MockCorpus.java
index 3b029e3..1a25626 100644
--- a/tests/src/com/android/quicksearchbox/MockCorpus.java
+++ b/tests/src/com/android/quicksearchbox/MockCorpus.java
@@ -155,4 +155,8 @@
return false;
}
+ public boolean isLocationAware() {
+ return false;
+ }
+
}
diff --git a/tests/src/com/android/quicksearchbox/MockSource.java b/tests/src/com/android/quicksearchbox/MockSource.java
index be56b29..ddf584c 100644
--- a/tests/src/com/android/quicksearchbox/MockSource.java
+++ b/tests/src/com/android/quicksearchbox/MockSource.java
@@ -105,6 +105,10 @@
return true;
}
+ public boolean isLocationAware() {
+ return false;
+ }
+
public SourceResult getSuggestions(String query, int queryLimit, boolean onlySource) {
if (query.length() == 0) {
return null;
diff --git a/tests/src/com/android/quicksearchbox/RankAwarePromoterTest.java b/tests/src/com/android/quicksearchbox/RankAwarePromoterTest.java
index 4fe3467..52ba9e6 100644
--- a/tests/src/com/android/quicksearchbox/RankAwarePromoterTest.java
+++ b/tests/src/com/android/quicksearchbox/RankAwarePromoterTest.java
@@ -20,8 +20,6 @@
import android.test.suitebuilder.annotation.SmallTest;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
/**
diff --git a/tests/src/com/android/quicksearchbox/SearchActivityTest.java b/tests/src/com/android/quicksearchbox/SearchActivityTest.java
index cee0317..b64549c 100644
--- a/tests/src/com/android/quicksearchbox/SearchActivityTest.java
+++ b/tests/src/com/android/quicksearchbox/SearchActivityTest.java
@@ -15,8 +15,6 @@
*/
package com.android.quicksearchbox;
-import com.android.quicksearchbox.SearchActivity;
-
import android.test.ActivityInstrumentationTestCase2;
/**
diff --git a/tests/src/com/android/quicksearchbox/SuggestionsProviderImplTest.java b/tests/src/com/android/quicksearchbox/SuggestionsProviderImplTest.java
index 89c2319..59b3c4a 100644
--- a/tests/src/com/android/quicksearchbox/SuggestionsProviderImplTest.java
+++ b/tests/src/com/android/quicksearchbox/SuggestionsProviderImplTest.java
@@ -43,7 +43,7 @@
mCorpora.addCorpus(MockCorpus.CORPUS_1);
mCorpora.addCorpus(MockCorpus.CORPUS_2);
CorpusRanker corpusRanker = new LexicographicalCorpusRanker(mCorpora);
- Logger logger = new MockLogger();
+ Logger logger = new NoLogger();
mProvider = new SuggestionsProviderImpl(config,
mTaskExecutor,
publishThread,