Merge change 3880 into donut
* changes: Run search dialog in the system process.
This commit is contained in:
@@ -76,6 +76,7 @@ LOCAL_SRC_FILES += \
|
||||
core/java/android/app/IIntentSender.aidl \
|
||||
core/java/android/app/INotificationManager.aidl \
|
||||
core/java/android/app/ISearchManager.aidl \
|
||||
core/java/android/app/ISearchManagerCallback.aidl \
|
||||
core/java/android/app/IServiceConnection.aidl \
|
||||
core/java/android/app/IStatusBar.aidl \
|
||||
core/java/android/app/IThumbnailReceiver.aidl \
|
||||
|
||||
@@ -628,6 +628,8 @@ public class Activity extends ContextThemeWrapper
|
||||
boolean mStartedActivity;
|
||||
/*package*/ int mConfigChangeFlags;
|
||||
/*package*/ Configuration mCurrentConfig;
|
||||
private SearchManager mSearchManager;
|
||||
private Bundle mSearchDialogState = null;
|
||||
|
||||
private Window mWindow;
|
||||
|
||||
@@ -788,6 +790,9 @@ public class Activity extends ContextThemeWrapper
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
mVisibleFromClient = mWindow.getWindowStyle().getBoolean(
|
||||
com.android.internal.R.styleable.Window_windowNoDisplay, true);
|
||||
// uses super.getSystemService() since this.getSystemService() looks at the
|
||||
// mSearchManager field.
|
||||
mSearchManager = (SearchManager) super.getSystemService(Context.SEARCH_SERVICE);
|
||||
mCalled = true;
|
||||
}
|
||||
|
||||
@@ -805,9 +810,10 @@ public class Activity extends ContextThemeWrapper
|
||||
|
||||
// Also restore the state of a search dialog (if any)
|
||||
// TODO more generic than just this manager
|
||||
SearchManager searchManager =
|
||||
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
|
||||
searchManager.restoreSearchDialog(savedInstanceState, SAVED_SEARCH_DIALOG_KEY);
|
||||
Bundle searchState = savedInstanceState.getBundle(SAVED_SEARCH_DIALOG_KEY);
|
||||
if (searchState != null) {
|
||||
mSearchManager.restoreSearchDialog(searchState);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1013,9 +1019,11 @@ public class Activity extends ContextThemeWrapper
|
||||
|
||||
// Also save the state of a search dialog (if any)
|
||||
// TODO more generic than just this manager
|
||||
SearchManager searchManager =
|
||||
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
|
||||
searchManager.saveSearchDialog(outState, SAVED_SEARCH_DIALOG_KEY);
|
||||
// onPause() should always be called before this method, so mSearchManagerState
|
||||
// should be up to date.
|
||||
if (mSearchDialogState != null) {
|
||||
outState.putBundle(SAVED_SEARCH_DIALOG_KEY, mSearchDialogState);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1286,12 +1294,6 @@ public class Activity extends ContextThemeWrapper
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// also dismiss search dialog if showing
|
||||
// TODO more generic than just this manager
|
||||
SearchManager searchManager =
|
||||
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
|
||||
searchManager.stopSearch();
|
||||
|
||||
// close any cursors we are managing.
|
||||
int numCursors = mManagedCursors.size();
|
||||
@@ -1301,6 +1303,10 @@ public class Activity extends ContextThemeWrapper
|
||||
c.mCursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Clear any search state saved in performPause(). If the state may be needed in the
|
||||
// future, it will have been saved by performSaveInstanceState()
|
||||
mSearchDialogState = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1324,9 +1330,7 @@ public class Activity extends ContextThemeWrapper
|
||||
|
||||
// also update search dialog if showing
|
||||
// TODO more generic than just this manager
|
||||
SearchManager searchManager =
|
||||
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
|
||||
searchManager.onConfigurationChanged(newConfig);
|
||||
mSearchManager.onConfigurationChanged(newConfig);
|
||||
|
||||
if (mWindow != null) {
|
||||
// Pass the configuration changed event to the window
|
||||
@@ -2543,10 +2547,7 @@ public class Activity extends ContextThemeWrapper
|
||||
*/
|
||||
public void startSearch(String initialQuery, boolean selectInitialQuery,
|
||||
Bundle appSearchData, boolean globalSearch) {
|
||||
// activate the search manager and start it up!
|
||||
SearchManager searchManager = (SearchManager)
|
||||
getSystemService(Context.SEARCH_SERVICE);
|
||||
searchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(),
|
||||
mSearchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(),
|
||||
appSearchData, globalSearch);
|
||||
}
|
||||
|
||||
@@ -3265,6 +3266,8 @@ public class Activity extends ContextThemeWrapper
|
||||
|
||||
if (WINDOW_SERVICE.equals(name)) {
|
||||
return mWindowManager;
|
||||
} else if (SEARCH_SERVICE.equals(name)) {
|
||||
return mSearchManager;
|
||||
}
|
||||
return super.getSystemService(name);
|
||||
}
|
||||
@@ -3563,10 +3566,21 @@ public class Activity extends ContextThemeWrapper
|
||||
"Activity " + mComponent.toShortString() +
|
||||
" did not call through to super.onPostResume()");
|
||||
}
|
||||
|
||||
// restore search dialog, if any
|
||||
if (mSearchDialogState != null) {
|
||||
mSearchManager.restoreSearchDialog(mSearchDialogState);
|
||||
}
|
||||
mSearchDialogState = null;
|
||||
}
|
||||
|
||||
final void performPause() {
|
||||
onPause();
|
||||
|
||||
// save search dialog state if the search dialog is open,
|
||||
// and then dismiss the search dialog
|
||||
mSearchDialogState = mSearchManager.saveSearchDialog();
|
||||
mSearchManager.stopSearch();
|
||||
}
|
||||
|
||||
final void performUserLeaving() {
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
package android.app;
|
||||
|
||||
import android.app.ISearchManagerCallback;
|
||||
import android.content.ComponentName;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.server.search.SearchableInfo;
|
||||
|
||||
/** @hide */
|
||||
@@ -26,4 +29,15 @@ interface ISearchManager {
|
||||
List<SearchableInfo> getSearchablesForWebSearch();
|
||||
SearchableInfo getDefaultSearchableForWebSearch();
|
||||
void setDefaultWebSearch(in ComponentName component);
|
||||
void startSearch(in String initialQuery,
|
||||
boolean selectInitialQuery,
|
||||
in ComponentName launchActivity,
|
||||
in Bundle appSearchData,
|
||||
boolean globalSearch,
|
||||
ISearchManagerCallback searchManagerCallback);
|
||||
void stopSearch();
|
||||
boolean isVisible();
|
||||
Bundle onSaveInstanceState();
|
||||
void onRestoreInstanceState(in Bundle savedInstanceState);
|
||||
void onConfigurationChanged(in Configuration newConfig);
|
||||
}
|
||||
|
||||
23
core/java/android/app/ISearchManagerCallback.aidl
Normal file
23
core/java/android/app/ISearchManagerCallback.aidl
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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 android.app;
|
||||
|
||||
/** @hide */
|
||||
oneway interface ISearchManagerCallback {
|
||||
void onDismiss();
|
||||
void onCancel();
|
||||
}
|
||||
@@ -76,8 +76,8 @@ import java.util.WeakHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* In-application-process implementation of Search Bar. This is still controlled by the
|
||||
* SearchManager, but it runs in the current activity's process to keep things lighter weight.
|
||||
* System search dialog. This is controlled by the
|
||||
* SearchManagerService and runs in the system process.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@@ -179,17 +179,17 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Window theWindow = getWindow();
|
||||
theWindow.setGravity(Gravity.TOP|Gravity.FILL_HORIZONTAL);
|
||||
|
||||
setContentView(com.android.internal.R.layout.search_bar);
|
||||
|
||||
theWindow.setLayout(ViewGroup.LayoutParams.FILL_PARENT,
|
||||
// taking up the whole window (even when transparent) is less than ideal,
|
||||
// but necessary to show the popup window until the window manager supports
|
||||
// having windows anchored by their parent but not clipped by them.
|
||||
ViewGroup.LayoutParams.FILL_PARENT);
|
||||
Window theWindow = getWindow();
|
||||
WindowManager.LayoutParams lp = theWindow.getAttributes();
|
||||
lp.type = WindowManager.LayoutParams.TYPE_SEARCH_BAR;
|
||||
lp.width = ViewGroup.LayoutParams.FILL_PARENT;
|
||||
// taking up the whole window (even when transparent) is less than ideal,
|
||||
// but necessary to show the popup window until the window manager supports
|
||||
// having windows anchored by their parent but not clipped by them.
|
||||
lp.height = ViewGroup.LayoutParams.FILL_PARENT;
|
||||
lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
|
||||
lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
|
||||
theWindow.setAttributes(lp);
|
||||
|
||||
@@ -234,10 +234,12 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
||||
|
||||
// Save voice intent for later queries/launching
|
||||
mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
|
||||
mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
|
||||
RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
|
||||
|
||||
mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
|
||||
mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
mLocationManager =
|
||||
(LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE);
|
||||
@@ -278,12 +280,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
||||
*/
|
||||
public boolean show(String initialQuery, boolean selectInitialQuery,
|
||||
ComponentName componentName, Bundle appSearchData, boolean globalSearch) {
|
||||
if (isShowing()) {
|
||||
// race condition - already showing but not handling events yet.
|
||||
// in this case, just discard the "show" request
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Reset any stored values from last time dialog was shown.
|
||||
mStoredComponentName = null;
|
||||
mStoredAppSearchData = null;
|
||||
@@ -442,11 +439,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
||||
|
||||
stopLocationUpdates();
|
||||
|
||||
// TODO: Removing the listeners means that they never get called, since
|
||||
// Dialog.dismissDialog() calls onStop() before sendDismissMessage().
|
||||
setOnCancelListener(null);
|
||||
setOnDismissListener(null);
|
||||
|
||||
// stop receiving broadcasts (throws exception if none registered)
|
||||
try {
|
||||
getContext().unregisterReceiver(mBroadcastReceiver);
|
||||
@@ -654,15 +646,15 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
||||
|
||||
mSearchAutoComplete.setDropDownAnimationStyle(0); // no animation
|
||||
mSearchAutoComplete.setThreshold(mSearchable.getSuggestThreshold());
|
||||
// we dismiss the entire dialog instead
|
||||
mSearchAutoComplete.setDropDownDismissedOnCompletion(false);
|
||||
|
||||
if (mGlobalSearchMode) {
|
||||
mSearchAutoComplete.setDropDownAlwaysVisible(true); // fill space until results come in
|
||||
mSearchAutoComplete.setDropDownDismissedOnCompletion(false);
|
||||
mSearchAutoComplete.setDropDownBackgroundResource(
|
||||
com.android.internal.R.drawable.search_dropdown_background);
|
||||
} else {
|
||||
mSearchAutoComplete.setDropDownAlwaysVisible(false);
|
||||
mSearchAutoComplete.setDropDownDismissedOnCompletion(true);
|
||||
mSearchAutoComplete.setDropDownBackgroundResource(
|
||||
com.android.internal.R.drawable.search_dropdown_background_apps);
|
||||
}
|
||||
@@ -1317,7 +1309,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches an intent. Also dismisses the search dialog if not in global search mode.
|
||||
* Launches an intent and dismisses the search dialog (unless the intent
|
||||
* is one of the special intents that modifies the state of the search dialog).
|
||||
*/
|
||||
private void launchIntent(Intent intent) {
|
||||
if (intent == null) {
|
||||
@@ -1326,9 +1319,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
||||
if (handleSpecialIntent(intent)){
|
||||
return;
|
||||
}
|
||||
if (!mGlobalSearchMode) {
|
||||
dismiss();
|
||||
}
|
||||
dismiss();
|
||||
getContext().startActivity(intent);
|
||||
}
|
||||
|
||||
@@ -1511,6 +1502,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
||||
int actionKey, String actionMsg) {
|
||||
// Now build the Intent
|
||||
Intent intent = new Intent(action);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
if (data != null) {
|
||||
intent.setData(data);
|
||||
}
|
||||
@@ -1595,14 +1587,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
||||
private boolean isEmpty() {
|
||||
return TextUtils.getTrimmedLength(getText()) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the entered text.
|
||||
*/
|
||||
private void clear() {
|
||||
setText("");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* We override this method to avoid replacing the query box text
|
||||
* when a suggestion is clicked.
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.server.search.SearchableInfo;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import java.util.List;
|
||||
@@ -1108,6 +1109,10 @@ import java.util.List;
|
||||
public class SearchManager
|
||||
implements DialogInterface.OnDismissListener, DialogInterface.OnCancelListener
|
||||
{
|
||||
|
||||
private static final boolean DBG = false;
|
||||
private static final String TAG = "SearchManager";
|
||||
|
||||
/**
|
||||
* This is a shortcut definition for the default menu key to use for invoking search.
|
||||
*
|
||||
@@ -1494,12 +1499,14 @@ public class SearchManager
|
||||
private static ISearchManager sService = getSearchManagerService();
|
||||
|
||||
private final Context mContext;
|
||||
private final Handler mHandler;
|
||||
|
||||
private SearchDialog mSearchDialog;
|
||||
|
||||
private OnDismissListener mDismissListener = null;
|
||||
private OnCancelListener mCancelListener = null;
|
||||
|
||||
// package private since they are used by the inner class SearchManagerCallback
|
||||
/* package */ boolean mIsShowing = false;
|
||||
/* package */ final Handler mHandler;
|
||||
/* package */ OnDismissListener mDismissListener = null;
|
||||
/* package */ OnCancelListener mCancelListener = null;
|
||||
|
||||
private final SearchManagerCallback mSearchManagerCallback = new SearchManagerCallback();
|
||||
|
||||
/*package*/ SearchManager(Context context, Handler handler) {
|
||||
mContext = context;
|
||||
@@ -1551,17 +1558,16 @@ public class SearchManager
|
||||
ComponentName launchActivity,
|
||||
Bundle appSearchData,
|
||||
boolean globalSearch) {
|
||||
|
||||
if (mSearchDialog == null) {
|
||||
mSearchDialog = new SearchDialog(mContext);
|
||||
if (DBG) debug("startSearch(), mIsShowing=" + mIsShowing);
|
||||
if (mIsShowing) return;
|
||||
try {
|
||||
mIsShowing = true;
|
||||
// activate the search manager and start it up!
|
||||
sService.startSearch(initialQuery, selectInitialQuery, launchActivity, appSearchData,
|
||||
globalSearch, mSearchManagerCallback);
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "startSearch() failed: " + ex);
|
||||
}
|
||||
|
||||
// activate the search manager and start it up!
|
||||
mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData,
|
||||
globalSearch);
|
||||
|
||||
mSearchDialog.setOnCancelListener(this);
|
||||
mSearchDialog.setOnDismissListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1575,9 +1581,16 @@ public class SearchManager
|
||||
*
|
||||
* @see #startSearch
|
||||
*/
|
||||
public void stopSearch() {
|
||||
if (mSearchDialog != null) {
|
||||
mSearchDialog.cancel();
|
||||
public void stopSearch() {
|
||||
if (DBG) debug("stopSearch(), mIsShowing=" + mIsShowing);
|
||||
if (!mIsShowing) return;
|
||||
try {
|
||||
sService.stopSearch();
|
||||
// onDismiss will also clear this, but we do it here too since onDismiss() is
|
||||
// called asynchronously.
|
||||
mIsShowing = false;
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "stopSearch() failed: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1590,13 +1603,11 @@ public class SearchManager
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public boolean isVisible() {
|
||||
if (mSearchDialog != null) {
|
||||
return mSearchDialog.isShowing();
|
||||
}
|
||||
return false;
|
||||
public boolean isVisible() {
|
||||
if (DBG) debug("isVisible(), mIsShowing=" + mIsShowing);
|
||||
return mIsShowing;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* See {@link SearchManager#setOnDismissListener} for configuring your activity to monitor
|
||||
* search UI state.
|
||||
@@ -1631,79 +1642,112 @@ public class SearchManager
|
||||
public void setOnDismissListener(final OnDismissListener listener) {
|
||||
mDismissListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* The callback from the search dialog when dismissed
|
||||
* @hide
|
||||
*/
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
if (dialog == mSearchDialog) {
|
||||
if (mDismissListener != null) {
|
||||
mDismissListener.onDismiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or clear the callback that will be invoked whenever the search UI is canceled.
|
||||
*
|
||||
* @param listener The {@link OnCancelListener} to use, or null.
|
||||
*/
|
||||
public void setOnCancelListener(final OnCancelListener listener) {
|
||||
public void setOnCancelListener(OnCancelListener listener) {
|
||||
mCancelListener = listener;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The callback from the search dialog when canceled
|
||||
* @hide
|
||||
*/
|
||||
|
||||
private class SearchManagerCallback extends ISearchManagerCallback.Stub {
|
||||
|
||||
private final Runnable mFireOnDismiss = new Runnable() {
|
||||
public void run() {
|
||||
if (DBG) debug("mFireOnDismiss");
|
||||
mIsShowing = false;
|
||||
if (mDismissListener != null) {
|
||||
mDismissListener.onDismiss();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable mFireOnCancel = new Runnable() {
|
||||
public void run() {
|
||||
if (DBG) debug("mFireOnCancel");
|
||||
// doesn't need to clear mIsShowing since onDismiss() always gets called too
|
||||
if (mCancelListener != null) {
|
||||
mCancelListener.onCancel();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void onDismiss() {
|
||||
if (DBG) debug("onDismiss()");
|
||||
mHandler.post(mFireOnDismiss);
|
||||
}
|
||||
|
||||
public void onCancel() {
|
||||
if (DBG) debug("onCancel()");
|
||||
mHandler.post(mFireOnCancel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: remove the DialogInterface interfaces from SearchManager.
|
||||
// This changes the public API, so I'll do it in a separate change.
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
if (dialog == mSearchDialog) {
|
||||
if (mCancelListener != null) {
|
||||
mCancelListener.onCancel();
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the state of the search UI.
|
||||
*
|
||||
* @return A Bundle containing the state of the search dialog, or {@code null}
|
||||
* if the search UI is not visible.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public Bundle saveSearchDialog() {
|
||||
if (DBG) debug("saveSearchDialog(), mIsShowing=" + mIsShowing);
|
||||
if (!mIsShowing) return null;
|
||||
try {
|
||||
return sService.onSaveInstanceState();
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "onSaveInstanceState() failed: " + ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save instance state so we can recreate after a rotation.
|
||||
*
|
||||
* Restores the state of the search dialog.
|
||||
*
|
||||
* @param searchDialogState Bundle to read the state from.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
void saveSearchDialog(Bundle outState, String key) {
|
||||
if (mSearchDialog != null && mSearchDialog.isShowing()) {
|
||||
Bundle searchDialogState = mSearchDialog.onSaveInstanceState();
|
||||
outState.putBundle(key, searchDialogState);
|
||||
public void restoreSearchDialog(Bundle searchDialogState) {
|
||||
if (DBG) debug("restoreSearchDialog(" + searchDialogState + ")");
|
||||
if (searchDialogState == null) return;
|
||||
try {
|
||||
sService.onRestoreInstanceState(searchDialogState);
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "onRestoreInstanceState() failed: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore instance state after a rotation.
|
||||
*
|
||||
* Update the search dialog after a configuration change.
|
||||
*
|
||||
* @param newConfig The new configuration.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
void restoreSearchDialog(Bundle inState, String key) {
|
||||
Bundle searchDialogState = inState.getBundle(key);
|
||||
if (searchDialogState != null) {
|
||||
if (mSearchDialog == null) {
|
||||
mSearchDialog = new SearchDialog(mContext);
|
||||
}
|
||||
mSearchDialog.onRestoreInstanceState(searchDialogState);
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
if (DBG) debug("onConfigurationChanged(" + newConfig + "), mIsShowing=" + mIsShowing);
|
||||
if (!mIsShowing) return;
|
||||
try {
|
||||
sService.onConfigurationChanged(newConfig);
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "onConfigurationChanged() failed:" + ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for updating layout on a rotation
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
void onConfigurationChanged(Configuration newConfig) {
|
||||
if (mSearchDialog != null && mSearchDialog.isShowing()) {
|
||||
mSearchDialog.onConfigurationChanged(newConfig);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static ISearchManager getSearchManagerService() {
|
||||
return ISearchManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.SEARCH_SERVICE));
|
||||
@@ -1724,7 +1768,8 @@ public class SearchManager
|
||||
boolean globalSearch) {
|
||||
try {
|
||||
return sService.getSearchableInfo(componentName, globalSearch);
|
||||
} catch (RemoteException e) {
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "getSearchableInfo() failed: " + ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1805,6 +1850,7 @@ public class SearchManager
|
||||
try {
|
||||
return sService.getSearchablesInGlobalSearch();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "getSearchablesInGlobalSearch() failed: " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1812,7 +1858,8 @@ public class SearchManager
|
||||
/**
|
||||
* Returns a list of the searchable activities that handle web searches.
|
||||
*
|
||||
* @return a a list of all searchable activities that handle {@link SearchManager#ACTION_WEB_SEARCH}.
|
||||
* @return a list of all searchable activities that handle
|
||||
* {@link android.content.Intent#ACTION_WEB_SEARCH}.
|
||||
*
|
||||
* @hide because SearchableInfo is not part of the API.
|
||||
*/
|
||||
@@ -1820,6 +1867,7 @@ public class SearchManager
|
||||
try {
|
||||
return sService.getSearchablesForWebSearch();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "getSearchablesForWebSearch() failed: " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1835,6 +1883,7 @@ public class SearchManager
|
||||
try {
|
||||
return sService.getDefaultSearchableForWebSearch();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "getDefaultSearchableForWebSearch() failed: " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1850,6 +1899,12 @@ public class SearchManager
|
||||
try {
|
||||
sService.setDefaultWebSearch(component);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "setDefaultWebSearch() failed: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void debug(String msg) {
|
||||
Thread thread = Thread.currentThread();
|
||||
Log.d(TAG, msg + " (" + thread.getName() + "-" + thread.getId() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,15 +17,25 @@
|
||||
package android.server.search;
|
||||
|
||||
import android.app.ISearchManager;
|
||||
import android.app.ISearchManagerCallback;
|
||||
import android.app.SearchDialog;
|
||||
import android.app.SearchManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
/**
|
||||
* This is a simplified version of the Search Manager service. It no longer handles
|
||||
@@ -34,16 +44,20 @@ import java.util.List;
|
||||
* invoked search) to specific searchable activities (where the search will be dispatched).
|
||||
*/
|
||||
public class SearchManagerService extends ISearchManager.Stub
|
||||
implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener
|
||||
{
|
||||
// general debugging support
|
||||
private static final String TAG = "SearchManagerService";
|
||||
private static final boolean DEBUG = false;
|
||||
private static final boolean DBG = false;
|
||||
|
||||
// class maintenance and general shared data
|
||||
private final Context mContext;
|
||||
private final Handler mHandler;
|
||||
private boolean mSearchablesDirty;
|
||||
private Searchables mSearchables;
|
||||
private final Searchables mSearchables;
|
||||
|
||||
final SearchDialog mSearchDialog;
|
||||
ISearchManagerCallback mCallback = null;
|
||||
|
||||
/**
|
||||
* Initializes the Search Manager service in the provided system context.
|
||||
@@ -56,6 +70,9 @@ public class SearchManagerService extends ISearchManager.Stub
|
||||
mHandler = new Handler();
|
||||
mSearchablesDirty = true;
|
||||
mSearchables = new Searchables(context);
|
||||
mSearchDialog = new SearchDialog(context);
|
||||
mSearchDialog.setOnCancelListener(this);
|
||||
mSearchDialog.setOnDismissListener(this);
|
||||
|
||||
// Setup the infrastructure for updating and maintaining the list
|
||||
// of searchable activities.
|
||||
@@ -107,6 +124,7 @@ public class SearchManagerService extends ISearchManager.Stub
|
||||
* a package add/remove broadcast message.
|
||||
*/
|
||||
private void updateSearchables() {
|
||||
if (DBG) debug("updateSearchables()");
|
||||
mSearchables.buildSearchableList();
|
||||
mSearchablesDirty = false;
|
||||
}
|
||||
@@ -137,6 +155,10 @@ public class SearchManagerService extends ISearchManager.Stub
|
||||
if (globalSearch) {
|
||||
si = mSearchables.getDefaultSearchable();
|
||||
} else {
|
||||
if (launchActivity == null) {
|
||||
Log.e(TAG, "getSearchableInfo(), activity == null");
|
||||
return null;
|
||||
}
|
||||
si = mSearchables.getSearchableInfo(launchActivity);
|
||||
}
|
||||
|
||||
@@ -150,6 +172,145 @@ public class SearchManagerService extends ISearchManager.Stub
|
||||
updateSearchablesIfDirty();
|
||||
return mSearchables.getSearchablesInGlobalSearchList();
|
||||
}
|
||||
/**
|
||||
* Launches the search UI on the main thread of the service.
|
||||
*
|
||||
* @see SearchManager#startSearch(String, boolean, ComponentName, Bundle, boolean)
|
||||
*/
|
||||
public void startSearch(final String initialQuery,
|
||||
final boolean selectInitialQuery,
|
||||
final ComponentName launchActivity,
|
||||
final Bundle appSearchData,
|
||||
final boolean globalSearch,
|
||||
final ISearchManagerCallback searchManagerCallback) {
|
||||
if (DBG) debug("startSearch()");
|
||||
Runnable task = new Runnable() {
|
||||
public void run() {
|
||||
performStartSearch(initialQuery,
|
||||
selectInitialQuery,
|
||||
launchActivity,
|
||||
appSearchData,
|
||||
globalSearch,
|
||||
searchManagerCallback);
|
||||
}
|
||||
};
|
||||
mHandler.post(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually launches the search. This must be called on the service UI thread.
|
||||
*/
|
||||
/*package*/ void performStartSearch(String initialQuery,
|
||||
boolean selectInitialQuery,
|
||||
ComponentName launchActivity,
|
||||
Bundle appSearchData,
|
||||
boolean globalSearch,
|
||||
ISearchManagerCallback searchManagerCallback) {
|
||||
if (DBG) debug("performStartSearch()");
|
||||
mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData,
|
||||
globalSearch);
|
||||
if (searchManagerCallback != null) {
|
||||
mCallback = searchManagerCallback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the search dialog. Can be called from any thread.
|
||||
*/
|
||||
public void stopSearch() {
|
||||
if (DBG) debug("stopSearch()");
|
||||
mHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
performStopSearch();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the search dialog. Must be called from the service UI thread.
|
||||
*/
|
||||
/*package*/ void performStopSearch() {
|
||||
if (DBG) debug("performStopSearch()");
|
||||
mSearchDialog.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the Search UI is currently displayed.
|
||||
*
|
||||
* @see SearchManager#isVisible()
|
||||
*/
|
||||
public boolean isVisible() {
|
||||
return postAndWait(mIsShowing, false, "isShowing()");
|
||||
}
|
||||
|
||||
private final Callable<Boolean> mIsShowing = new Callable<Boolean>() {
|
||||
public Boolean call() {
|
||||
return mSearchDialog.isShowing();
|
||||
}
|
||||
};
|
||||
|
||||
public Bundle onSaveInstanceState() {
|
||||
return postAndWait(mOnSaveInstanceState, null, "onSaveInstanceState()");
|
||||
}
|
||||
|
||||
private final Callable<Bundle> mOnSaveInstanceState = new Callable<Bundle>() {
|
||||
public Bundle call() {
|
||||
if (mSearchDialog.isShowing()) {
|
||||
return mSearchDialog.onSaveInstanceState();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void onRestoreInstanceState(final Bundle searchDialogState) {
|
||||
if (searchDialogState != null) {
|
||||
mHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
mSearchDialog.onRestoreInstanceState(searchDialogState);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void onConfigurationChanged(final Configuration newConfig) {
|
||||
mHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
if (mSearchDialog.isShowing()) {
|
||||
mSearchDialog.onConfigurationChanged(newConfig);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@link SearchDialog} when it goes away.
|
||||
*/
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
if (DBG) debug("onDismiss()");
|
||||
if (mCallback != null) {
|
||||
try {
|
||||
mCallback.onDismiss();
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "onDismiss() failed: " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@link SearchDialog} when the user or activity cancels search.
|
||||
* When this is called, {@link #onDismiss} is called too.
|
||||
*/
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
if (DBG) debug("onCancel()");
|
||||
if (mCallback != null) {
|
||||
try {
|
||||
mCallback.onCancel();
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "onCancel() failed: " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of the searchable activities that handle web searches.
|
||||
@@ -173,4 +334,34 @@ public class SearchManagerService extends ISearchManager.Stub
|
||||
public void setDefaultWebSearch(ComponentName component) {
|
||||
mSearchables.setDefaultWebSearch(component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs an operation on the handler for the service, blocks until it returns,
|
||||
* and returns the value returned by the operation.
|
||||
*
|
||||
* @param <V> Return value type.
|
||||
* @param callable Operation to run.
|
||||
* @param errorResult Value to return if the operations throws an exception.
|
||||
* @param name Operation name to include in error log messages.
|
||||
* @return The value returned by the operation.
|
||||
*/
|
||||
private <V> V postAndWait(Callable<V> callable, V errorResult, String name) {
|
||||
FutureTask<V> task = new FutureTask<V>(callable);
|
||||
mHandler.post(task);
|
||||
try {
|
||||
return task.get();
|
||||
} catch (InterruptedException ex) {
|
||||
Log.e(TAG, "Error calling " + name + ": " + ex);
|
||||
return errorResult;
|
||||
} catch (ExecutionException ex) {
|
||||
Log.e(TAG, "Error calling " + name + ": " + ex);
|
||||
return errorResult;
|
||||
}
|
||||
}
|
||||
|
||||
private static void debug(String msg) {
|
||||
Thread thread = Thread.currentThread();
|
||||
Log.d(TAG, msg + " (" + thread.getName() + "-" + thread.getId() + ")");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -320,7 +320,7 @@ public final class SearchableInfo implements Parcelable {
|
||||
|
||||
// for now, implement some form of rules - minimal data
|
||||
if (mLabelId == 0) {
|
||||
throw new IllegalArgumentException("No label.");
|
||||
throw new IllegalArgumentException("Search label must be a resource reference.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,13 +441,17 @@ public final class SearchableInfo implements Parcelable {
|
||||
xml.close();
|
||||
|
||||
if (DBG) {
|
||||
Log.d(LOG_TAG, "Checked " + activityInfo.name
|
||||
+ ",label=" + searchable.getLabelId()
|
||||
+ ",icon=" + searchable.getIconId()
|
||||
+ ",suggestAuthority=" + searchable.getSuggestAuthority()
|
||||
+ ",target=" + searchable.getSearchActivity().getClassName()
|
||||
+ ",global=" + searchable.shouldIncludeInGlobalSearch()
|
||||
+ ",threshold=" + searchable.getSuggestThreshold());
|
||||
if (searchable != null) {
|
||||
Log.d(LOG_TAG, "Checked " + activityInfo.name
|
||||
+ ",label=" + searchable.getLabelId()
|
||||
+ ",icon=" + searchable.getIconId()
|
||||
+ ",suggestAuthority=" + searchable.getSuggestAuthority()
|
||||
+ ",target=" + searchable.getSearchActivity().getClassName()
|
||||
+ ",global=" + searchable.shouldIncludeInGlobalSearch()
|
||||
+ ",threshold=" + searchable.getSuggestThreshold());
|
||||
} else {
|
||||
Log.d(LOG_TAG, "Checked " + activityInfo.name + ", no searchable meta-data");
|
||||
}
|
||||
}
|
||||
return searchable;
|
||||
}
|
||||
|
||||
@@ -219,7 +219,20 @@
|
||||
</service>
|
||||
|
||||
<!-- Application components used for search manager tests -->
|
||||
<!-- TODO: Removed temporarily - need to be replaced using mocks -->
|
||||
|
||||
<activity android:name=".SearchableActivity"
|
||||
android:label="Searchable Activity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.app.searchable"
|
||||
android:resource="@xml/searchable" />
|
||||
</activity>
|
||||
|
||||
<provider android:name=".SuggestionProvider"
|
||||
android:authorities="com.android.unit_tests.SuggestionProvider">
|
||||
</provider>
|
||||
|
||||
<!-- Used to test IPC. -->
|
||||
<service android:name=".binder.BinderTestService"
|
||||
|
||||
@@ -50,5 +50,8 @@
|
||||
<item quantity="other">Some dogs</item>
|
||||
</plurals>
|
||||
|
||||
<string name="searchable_label">SearchManager Test</string>
|
||||
<string name="searchable_hint">A search hint</string>
|
||||
|
||||
<!-- <string name="layout_six_text_text">F</string> -->
|
||||
</resources>
|
||||
|
||||
@@ -15,7 +15,12 @@
|
||||
-->
|
||||
|
||||
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:label="SearchManagerTest"
|
||||
android:hint="SearchManagerTest Hint"
|
||||
/>
|
||||
android:label="@string/searchable_label"
|
||||
android:hint="@string/searchable_hint"
|
||||
android:searchSuggestAuthority="com.android.unit_tests.SuggestionProvider"
|
||||
>
|
||||
|
||||
<actionkey android:keycode="KEYCODE_CALL"
|
||||
android:suggestActionMsgColumn="suggest_action_msg_call" />
|
||||
|
||||
</searchable>
|
||||
@@ -23,7 +23,10 @@ import android.app.ISearchManager;
|
||||
import android.app.SearchManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.server.search.SearchableInfo;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
@@ -37,12 +40,11 @@ import android.util.AndroidRuntimeException;
|
||||
* com.android.unit_tests/android.test.InstrumentationTestRunner
|
||||
*/
|
||||
public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalActivity> {
|
||||
|
||||
// If non-zero, enable a set of tests that start and stop the search manager.
|
||||
// This is currently disabled because it's causing an unwanted jump from the unit test
|
||||
// activity into the contacts activity. We'll put this back after we disable that jump.
|
||||
private static final int TEST_SEARCH_START = 0;
|
||||
|
||||
|
||||
private ComponentName SEARCHABLE_ACTIVITY =
|
||||
new ComponentName("com.android.unit_tests",
|
||||
"com.android.unit_tests.SearchableActivity");
|
||||
|
||||
/*
|
||||
* Bug list of test ideas.
|
||||
*
|
||||
@@ -88,7 +90,30 @@ public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalAct
|
||||
super.setUp();
|
||||
|
||||
Activity testActivity = getActivity();
|
||||
mContext = (Context)testActivity;
|
||||
mContext = testActivity;
|
||||
}
|
||||
|
||||
private ISearchManager getSearchManagerService() {
|
||||
return ISearchManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.SEARCH_SERVICE));
|
||||
}
|
||||
|
||||
// Checks that the search UI is visible.
|
||||
private void assertSearchVisible() {
|
||||
SearchManager searchManager = (SearchManager)
|
||||
mContext.getSystemService(Context.SEARCH_SERVICE);
|
||||
assertTrue("SearchManager thinks search UI isn't visible when it should be",
|
||||
searchManager.isVisible());
|
||||
}
|
||||
|
||||
// Checks that the search UI is not visible.
|
||||
// This checks both the SearchManager and the SearchManagerService,
|
||||
// since SearchManager keeps a local variable for the visibility.
|
||||
private void assertSearchNotVisible() {
|
||||
SearchManager searchManager = (SearchManager)
|
||||
mContext.getSystemService(Context.SEARCH_SERVICE);
|
||||
assertFalse("SearchManager thinks search UI is visible when it shouldn't be",
|
||||
searchManager.isVisible());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,9 +122,7 @@ public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalAct
|
||||
*/
|
||||
@MediumTest
|
||||
public void testSearchManagerInterfaceAvailable() {
|
||||
ISearchManager searchManager1 = ISearchManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.SEARCH_SERVICE));
|
||||
assertNotNull(searchManager1);
|
||||
assertNotNull(getSearchManagerService());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,38 +158,127 @@ public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalAct
|
||||
SearchManager searchManager2 = (SearchManager)
|
||||
mContext.getSystemService(Context.SEARCH_SERVICE);
|
||||
assertNotNull(searchManager2);
|
||||
assertSame( searchManager1, searchManager2 );
|
||||
assertSame(searchManager1, searchManager2 );
|
||||
}
|
||||
|
||||
|
||||
@MediumTest
|
||||
public void testSearchables() {
|
||||
SearchableInfo si;
|
||||
|
||||
si = SearchManager.getSearchableInfo(SEARCHABLE_ACTIVITY, false);
|
||||
assertNotNull(si);
|
||||
assertFalse(SearchManager.isDefaultSearchable(si));
|
||||
si = SearchManager.getSearchableInfo(SEARCHABLE_ACTIVITY, true);
|
||||
assertNotNull(si);
|
||||
assertTrue(SearchManager.isDefaultSearchable(si));
|
||||
si = SearchManager.getSearchableInfo(null, true);
|
||||
assertNotNull(si);
|
||||
assertTrue(SearchManager.isDefaultSearchable(si));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that rapid calls to start-stop-start doesn't cause problems.
|
||||
*/
|
||||
@MediumTest
|
||||
public void testSearchManagerFastInvocations() throws Exception {
|
||||
SearchManager searchManager = (SearchManager)
|
||||
mContext.getSystemService(Context.SEARCH_SERVICE);
|
||||
assertNotNull(searchManager);
|
||||
assertSearchNotVisible();
|
||||
|
||||
searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false);
|
||||
assertSearchVisible();
|
||||
searchManager.stopSearch();
|
||||
searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false);
|
||||
searchManager.stopSearch();
|
||||
assertSearchNotVisible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that startSearch() is idempotent.
|
||||
*/
|
||||
@MediumTest
|
||||
public void testStartSearchIdempotent() throws Exception {
|
||||
SearchManager searchManager = (SearchManager)
|
||||
mContext.getSystemService(Context.SEARCH_SERVICE);
|
||||
assertNotNull(searchManager);
|
||||
assertSearchNotVisible();
|
||||
|
||||
searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false);
|
||||
searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false);
|
||||
assertSearchVisible();
|
||||
searchManager.stopSearch();
|
||||
assertSearchNotVisible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that stopSearch() is idempotent and can be called when the search UI is not visible.
|
||||
*/
|
||||
@MediumTest
|
||||
public void testStopSearchIdempotent() throws Exception {
|
||||
SearchManager searchManager = (SearchManager)
|
||||
mContext.getSystemService(Context.SEARCH_SERVICE);
|
||||
assertNotNull(searchManager);
|
||||
assertSearchNotVisible();
|
||||
searchManager.stopSearch();
|
||||
assertSearchNotVisible();
|
||||
|
||||
searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false);
|
||||
assertSearchVisible();
|
||||
searchManager.stopSearch();
|
||||
searchManager.stopSearch();
|
||||
assertSearchNotVisible();
|
||||
}
|
||||
|
||||
/**
|
||||
* The goal of this test is to confirm that we can start and then
|
||||
* stop a simple search.
|
||||
*/
|
||||
|
||||
@MediumTest
|
||||
public void testSearchManagerInvocations() {
|
||||
@MediumTest
|
||||
public void testSearchManagerInvocations() throws Exception {
|
||||
SearchManager searchManager = (SearchManager)
|
||||
mContext.getSystemService(Context.SEARCH_SERVICE);
|
||||
assertNotNull(searchManager);
|
||||
|
||||
// TODO: make a real component name, or remove this need
|
||||
final ComponentName cn = new ComponentName("", "");
|
||||
assertSearchNotVisible();
|
||||
|
||||
if (TEST_SEARCH_START != 0) {
|
||||
// These tests should simply run to completion w/o exceptions
|
||||
searchManager.startSearch(null, false, cn, null, false);
|
||||
searchManager.stopSearch();
|
||||
|
||||
searchManager.startSearch("", false, cn, null, false);
|
||||
searchManager.stopSearch();
|
||||
|
||||
searchManager.startSearch("test search string", false, cn, null, false);
|
||||
searchManager.stopSearch();
|
||||
|
||||
searchManager.startSearch("test search string", true, cn, null, false);
|
||||
searchManager.stopSearch();
|
||||
}
|
||||
}
|
||||
// These tests should simply run to completion w/o exceptions
|
||||
searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false);
|
||||
assertSearchVisible();
|
||||
searchManager.stopSearch();
|
||||
assertSearchNotVisible();
|
||||
|
||||
searchManager.startSearch("", false, SEARCHABLE_ACTIVITY, null, false);
|
||||
assertSearchVisible();
|
||||
searchManager.stopSearch();
|
||||
assertSearchNotVisible();
|
||||
|
||||
searchManager.startSearch("test search string", false, SEARCHABLE_ACTIVITY, null, false);
|
||||
assertSearchVisible();
|
||||
searchManager.stopSearch();
|
||||
assertSearchNotVisible();
|
||||
|
||||
searchManager.startSearch("test search string", true, SEARCHABLE_ACTIVITY, null, false);
|
||||
assertSearchVisible();
|
||||
searchManager.stopSearch();
|
||||
assertSearchNotVisible();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testSearchDialogState() throws Exception {
|
||||
SearchManager searchManager = (SearchManager)
|
||||
mContext.getSystemService(Context.SEARCH_SERVICE);
|
||||
assertNotNull(searchManager);
|
||||
|
||||
Bundle searchState;
|
||||
|
||||
// search dialog not visible, so no state should be stored
|
||||
searchState = searchManager.saveSearchDialog();
|
||||
assertNull(searchState);
|
||||
|
||||
searchManager.startSearch("test search string", true, SEARCHABLE_ACTIVITY, null, false);
|
||||
searchState = searchManager.saveSearchDialog();
|
||||
assertNotNull(searchState);
|
||||
searchManager.stopSearch();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.unit_tests;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class SearchableActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
finish();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.unit_tests;
|
||||
|
||||
import android.app.SearchManager;
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.net.Uri;
|
||||
|
||||
/** Simple test provider that runs in the local process.
|
||||
*
|
||||
* Used by {@link SearchManagerTest}.
|
||||
*/
|
||||
public class SuggestionProvider extends ContentProvider {
|
||||
private static final String TAG = "SuggestionProvider";
|
||||
|
||||
private static final int SEARCH_SUGGESTIONS = 1;
|
||||
|
||||
private static final UriMatcher sURLMatcher = new UriMatcher(
|
||||
UriMatcher.NO_MATCH);
|
||||
|
||||
static {
|
||||
sURLMatcher.addURI("*", SearchManager.SUGGEST_URI_PATH_QUERY,
|
||||
SEARCH_SUGGESTIONS);
|
||||
sURLMatcher.addURI("*", SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
|
||||
SEARCH_SUGGESTIONS);
|
||||
}
|
||||
|
||||
private static final String[] COLUMNS = new String[] {
|
||||
"_id",
|
||||
SearchManager.SUGGEST_COLUMN_TEXT_1,
|
||||
SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
|
||||
SearchManager.SUGGEST_COLUMN_QUERY
|
||||
};
|
||||
|
||||
public SuggestionProvider() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri url, String[] projectionIn, String selection,
|
||||
String[] selectionArgs, String sort) {
|
||||
int match = sURLMatcher.match(url);
|
||||
switch (match) {
|
||||
case SEARCH_SUGGESTIONS:
|
||||
String query = url.getLastPathSegment();
|
||||
MatrixCursor cursor = new MatrixCursor(COLUMNS);
|
||||
String[] suffixes = { "", "a", " foo", "XXXXXXXXXXXXXXXXX" };
|
||||
for (String suffix : suffixes) {
|
||||
addRow(cursor, query + suffix);
|
||||
}
|
||||
return cursor;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URL: " + url);
|
||||
}
|
||||
}
|
||||
|
||||
private void addRow(MatrixCursor cursor, String string) {
|
||||
long id = cursor.getCount();
|
||||
cursor.newRow().add(id).add(string).add(Intent.ACTION_SEARCH).add(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(Uri url) {
|
||||
int match = sURLMatcher.match(url);
|
||||
switch (match) {
|
||||
case SEARCH_SUGGESTIONS:
|
||||
return SearchManager.SUGGEST_MIME_TYPE;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URL: " + url);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
|
||||
throw new UnsupportedOperationException("update not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri url, ContentValues initialValues) {
|
||||
throw new UnsupportedOperationException("insert not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri url, String where, String[] whereArgs) {
|
||||
throw new UnsupportedOperationException("delete not supported");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user