Merge change 6284 into donut
* changes: Run search UI on its own thread.
This commit is contained in:
@@ -606,7 +606,6 @@ public class Activity extends ContextThemeWrapper
|
|||||||
private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds";
|
private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds";
|
||||||
private static final String SAVED_DIALOGS_TAG = "android:savedDialogs";
|
private static final String SAVED_DIALOGS_TAG = "android:savedDialogs";
|
||||||
private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_";
|
private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_";
|
||||||
private static final String SAVED_SEARCH_DIALOG_KEY = "android:search_dialog";
|
|
||||||
|
|
||||||
private SparseArray<Dialog> mManagedDialogs;
|
private SparseArray<Dialog> mManagedDialogs;
|
||||||
|
|
||||||
@@ -630,7 +629,6 @@ public class Activity extends ContextThemeWrapper
|
|||||||
/*package*/ int mConfigChangeFlags;
|
/*package*/ int mConfigChangeFlags;
|
||||||
/*package*/ Configuration mCurrentConfig;
|
/*package*/ Configuration mCurrentConfig;
|
||||||
private SearchManager mSearchManager;
|
private SearchManager mSearchManager;
|
||||||
private Bundle mSearchDialogState = null;
|
|
||||||
|
|
||||||
private Window mWindow;
|
private Window mWindow;
|
||||||
|
|
||||||
@@ -808,13 +806,6 @@ public class Activity extends ContextThemeWrapper
|
|||||||
final void performRestoreInstanceState(Bundle savedInstanceState) {
|
final void performRestoreInstanceState(Bundle savedInstanceState) {
|
||||||
onRestoreInstanceState(savedInstanceState);
|
onRestoreInstanceState(savedInstanceState);
|
||||||
restoreManagedDialogs(savedInstanceState);
|
restoreManagedDialogs(savedInstanceState);
|
||||||
|
|
||||||
// Also restore the state of a search dialog (if any)
|
|
||||||
// TODO more generic than just this manager
|
|
||||||
Bundle searchState = savedInstanceState.getBundle(SAVED_SEARCH_DIALOG_KEY);
|
|
||||||
if (searchState != null) {
|
|
||||||
mSearchManager.restoreSearchDialog(searchState);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1030,14 +1021,6 @@ public class Activity extends ContextThemeWrapper
|
|||||||
final void performSaveInstanceState(Bundle outState) {
|
final void performSaveInstanceState(Bundle outState) {
|
||||||
onSaveInstanceState(outState);
|
onSaveInstanceState(outState);
|
||||||
saveManagedDialogs(outState);
|
saveManagedDialogs(outState);
|
||||||
|
|
||||||
// Also save the state of a search dialog (if any)
|
|
||||||
// TODO more generic than just this manager
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1317,10 +1300,6 @@ public class Activity extends ContextThemeWrapper
|
|||||||
c.mCursor.close();
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1342,10 +1321,6 @@ public class Activity extends ContextThemeWrapper
|
|||||||
public void onConfigurationChanged(Configuration newConfig) {
|
public void onConfigurationChanged(Configuration newConfig) {
|
||||||
mCalled = true;
|
mCalled = true;
|
||||||
|
|
||||||
// also update search dialog if showing
|
|
||||||
// TODO more generic than just this manager
|
|
||||||
mSearchManager.onConfigurationChanged(newConfig);
|
|
||||||
|
|
||||||
if (mWindow != null) {
|
if (mWindow != null) {
|
||||||
// Pass the configuration changed event to the window
|
// Pass the configuration changed event to the window
|
||||||
mWindow.onConfigurationChanged(newConfig);
|
mWindow.onConfigurationChanged(newConfig);
|
||||||
@@ -3575,20 +3550,12 @@ public class Activity extends ContextThemeWrapper
|
|||||||
"Activity " + mComponent.toShortString() +
|
"Activity " + mComponent.toShortString() +
|
||||||
" did not call through to super.onPostResume()");
|
" did not call through to super.onPostResume()");
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore search dialog, if any
|
|
||||||
if (mSearchDialogState != null) {
|
|
||||||
mSearchManager.restoreSearchDialog(mSearchDialogState);
|
|
||||||
}
|
|
||||||
mSearchDialogState = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final void performPause() {
|
final void performPause() {
|
||||||
onPause();
|
onPause();
|
||||||
|
|
||||||
// save search dialog state if the search dialog is open,
|
// dismiss the search dialog if it is open
|
||||||
// and then dismiss the search dialog
|
|
||||||
mSearchDialogState = mSearchManager.saveSearchDialog();
|
|
||||||
mSearchManager.stopSearch();
|
mSearchManager.stopSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,4 @@ interface ISearchManager {
|
|||||||
boolean globalSearch,
|
boolean globalSearch,
|
||||||
ISearchManagerCallback searchManagerCallback);
|
ISearchManagerCallback searchManagerCallback);
|
||||||
void stopSearch();
|
void stopSearch();
|
||||||
boolean isVisible();
|
|
||||||
Bundle onSaveInstanceState();
|
|
||||||
void onRestoreInstanceState(in Bundle savedInstanceState);
|
|
||||||
void onConfigurationChanged(in Configuration newConfig);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,13 +19,11 @@ package android.app;
|
|||||||
import static android.app.SuggestionsAdapter.getColumnString;
|
import static android.app.SuggestionsAdapter.getColumnString;
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
@@ -33,8 +31,8 @@ import android.content.pm.PackageManager.NameNotFoundException;
|
|||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.graphics.drawable.Animatable;
|
import android.graphics.drawable.Animatable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
@@ -96,10 +94,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
private static final int SEARCH_PLATE_LEFT_PADDING_GLOBAL = 12;
|
private static final int SEARCH_PLATE_LEFT_PADDING_GLOBAL = 12;
|
||||||
private static final int SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL = 7;
|
private static final int SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL = 7;
|
||||||
|
|
||||||
// interaction with runtime
|
|
||||||
private IntentFilter mCloseDialogsFilter;
|
|
||||||
private IntentFilter mPackageFilter;
|
|
||||||
|
|
||||||
// views & widgets
|
// views & widgets
|
||||||
private TextView mBadgeLabel;
|
private TextView mBadgeLabel;
|
||||||
private ImageView mAppIcon;
|
private ImageView mAppIcon;
|
||||||
@@ -211,14 +205,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
// Touching outside of the search dialog will dismiss it
|
// Touching outside of the search dialog will dismiss it
|
||||||
setCanceledOnTouchOutside(true);
|
setCanceledOnTouchOutside(true);
|
||||||
|
|
||||||
// Set up broadcast filters
|
|
||||||
mCloseDialogsFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
|
|
||||||
mPackageFilter = new IntentFilter();
|
|
||||||
mPackageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
|
||||||
mPackageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
|
||||||
mPackageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
|
|
||||||
mPackageFilter.addDataScheme("package");
|
|
||||||
|
|
||||||
// Save voice intent for later queries/launching
|
// Save voice intent for later queries/launching
|
||||||
mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
|
mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
|
||||||
mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
@@ -383,15 +369,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStart() {
|
|
||||||
super.onStart();
|
|
||||||
|
|
||||||
// receive broadcasts
|
|
||||||
getContext().registerReceiver(mBroadcastReceiver, mCloseDialogsFilter);
|
|
||||||
getContext().registerReceiver(mBroadcastReceiver, mPackageFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The search dialog is being dismissed, so handle all of the local shutdown operations.
|
* The search dialog is being dismissed, so handle all of the local shutdown operations.
|
||||||
*
|
*
|
||||||
@@ -402,13 +379,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
public void onStop() {
|
public void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
|
|
||||||
// stop receiving broadcasts (throws exception if none registered)
|
|
||||||
try {
|
|
||||||
getContext().unregisterReceiver(mBroadcastReceiver);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
// This is OK - it just means we didn't have any registered
|
|
||||||
}
|
|
||||||
|
|
||||||
closeSuggestionsAdapter();
|
closeSuggestionsAdapter();
|
||||||
|
|
||||||
// dump extra memory we're hanging on to
|
// dump extra memory we're hanging on to
|
||||||
@@ -455,10 +425,13 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
/**
|
/**
|
||||||
* Save the minimal set of data necessary to recreate the search
|
* Save the minimal set of data necessary to recreate the search
|
||||||
*
|
*
|
||||||
* @return A bundle with the state of the dialog.
|
* @return A bundle with the state of the dialog, or {@code null} if the search
|
||||||
|
* dialog is not showing.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Bundle onSaveInstanceState() {
|
public Bundle onSaveInstanceState() {
|
||||||
|
if (!isShowing()) return null;
|
||||||
|
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
|
|
||||||
// setup info so I can recreate this particular search
|
// setup info so I can recreate this particular search
|
||||||
@@ -483,6 +456,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onRestoreInstanceState(Bundle savedInstanceState) {
|
public void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||||
|
if (savedInstanceState == null) return;
|
||||||
|
|
||||||
ComponentName launchComponent = savedInstanceState.getParcelable(INSTANCE_KEY_COMPONENT);
|
ComponentName launchComponent = savedInstanceState.getParcelable(INSTANCE_KEY_COMPONENT);
|
||||||
Bundle appSearchData = savedInstanceState.getBundle(INSTANCE_KEY_APPDATA);
|
Bundle appSearchData = savedInstanceState.getBundle(INSTANCE_KEY_APPDATA);
|
||||||
boolean globalSearch = savedInstanceState.getBoolean(INSTANCE_KEY_GLOBALSEARCH);
|
boolean globalSearch = savedInstanceState.getBoolean(INSTANCE_KEY_GLOBALSEARCH);
|
||||||
@@ -509,7 +484,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
/**
|
/**
|
||||||
* Called after resources have changed, e.g. after screen rotation or locale change.
|
* Called after resources have changed, e.g. after screen rotation or locale change.
|
||||||
*/
|
*/
|
||||||
public void onConfigurationChanged(Configuration newConfig) {
|
public void onConfigurationChanged() {
|
||||||
if (isShowing()) {
|
if (isShowing()) {
|
||||||
// Redraw (resources may have changed)
|
// Redraw (resources may have changed)
|
||||||
updateSearchButton();
|
updateSearchButton();
|
||||||
@@ -1015,34 +990,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* When the ACTION_CLOSE_SYSTEM_DIALOGS intent is received, we should close ourselves
|
|
||||||
* immediately, in order to allow a higher-priority UI to take over
|
|
||||||
* (e.g. phone call received).
|
|
||||||
*
|
|
||||||
* When a package is added, removed or changed, our current context
|
|
||||||
* may no longer be valid. This would only happen if a package is installed/removed exactly
|
|
||||||
* when the search bar is open. So for now we're just going to close the search
|
|
||||||
* bar.
|
|
||||||
* Anything fancier would require some checks to see if the user's context was still valid.
|
|
||||||
* Which would be messier.
|
|
||||||
*/
|
|
||||||
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
String action = intent.getAction();
|
|
||||||
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
|
|
||||||
cancel();
|
|
||||||
} else if (Intent.ACTION_PACKAGE_ADDED.equals(action)
|
|
||||||
|| Intent.ACTION_PACKAGE_REMOVED.equals(action)
|
|
||||||
|| Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
|
|
||||||
cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
|
if (!isShowing()) return;
|
||||||
|
|
||||||
// We made sure the IME was displayed, so also make sure it is closed
|
// We made sure the IME was displayed, so also make sure it is closed
|
||||||
// when we go away.
|
// when we go away.
|
||||||
InputMethodManager imm = (InputMethodManager)getContext()
|
InputMethodManager imm = (InputMethodManager)getContext()
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import android.content.ComponentName;
|
|||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -1729,59 +1728,6 @@ public class SearchManager
|
|||||||
throw new UnsupportedOperationException();
|
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 mService.onSaveInstanceState();
|
|
||||||
} catch (RemoteException ex) {
|
|
||||||
Log.e(TAG, "onSaveInstanceState() failed: " + ex);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restores the state of the search dialog.
|
|
||||||
*
|
|
||||||
* @param searchDialogState Bundle to read the state from.
|
|
||||||
*
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public void restoreSearchDialog(Bundle searchDialogState) {
|
|
||||||
if (DBG) debug("restoreSearchDialog(" + searchDialogState + ")");
|
|
||||||
if (searchDialogState == null) return;
|
|
||||||
try {
|
|
||||||
mService.onRestoreInstanceState(searchDialogState);
|
|
||||||
} catch (RemoteException ex) {
|
|
||||||
Log.e(TAG, "onRestoreInstanceState() failed: " + ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the search dialog after a configuration change.
|
|
||||||
*
|
|
||||||
* @param newConfig The new configuration.
|
|
||||||
*
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public void onConfigurationChanged(Configuration newConfig) {
|
|
||||||
if (DBG) debug("onConfigurationChanged(" + newConfig + "), mIsShowing=" + mIsShowing);
|
|
||||||
if (!mIsShowing) return;
|
|
||||||
try {
|
|
||||||
mService.onConfigurationChanged(newConfig);
|
|
||||||
} catch (RemoteException ex) {
|
|
||||||
Log.e(TAG, "onConfigurationChanged() failed:" + ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets information about a searchable activity. This method is static so that it can
|
* Gets information about a searchable activity. This method is static so that it can
|
||||||
* be used from non-Activity contexts.
|
* be used from non-Activity contexts.
|
||||||
|
|||||||
320
core/java/android/server/search/SearchDialogWrapper.java
Normal file
320
core/java/android/server/search/SearchDialogWrapper.java
Normal file
@@ -0,0 +1,320 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 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.server.search;
|
||||||
|
|
||||||
|
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.os.Bundle;
|
||||||
|
import android.os.DeadObjectException;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.HandlerThread;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.os.SystemProperties;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs an instance of {@link SearchDialog} on its own thread.
|
||||||
|
*/
|
||||||
|
class SearchDialogWrapper
|
||||||
|
implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
|
||||||
|
|
||||||
|
private static final String TAG = "SearchManagerService";
|
||||||
|
private static final boolean DBG = false;
|
||||||
|
|
||||||
|
private static final String DISABLE_SEARCH_PROPERTY = "dev.disablesearchdialog";
|
||||||
|
|
||||||
|
private static final String SEARCH_UI_THREAD_NAME = "SearchDialog";
|
||||||
|
private static final int SEARCH_UI_THREAD_PRIORITY =
|
||||||
|
android.os.Process.THREAD_PRIORITY_FOREGROUND;
|
||||||
|
|
||||||
|
// Takes no arguments
|
||||||
|
private static final int MSG_INIT = 0;
|
||||||
|
// Takes these arguments:
|
||||||
|
// arg1: selectInitialQuery, 0 = false, 1 = true
|
||||||
|
// arg2: globalSearch, 0 = false, 1 = true
|
||||||
|
// obj: searchManagerCallback
|
||||||
|
// data[KEY_INITIAL_QUERY]: initial query
|
||||||
|
// data[KEY_LAUNCH_ACTIVITY]: launch activity
|
||||||
|
// data[KEY_APP_SEARCH_DATA]: app search data
|
||||||
|
private static final int MSG_START_SEARCH = 1;
|
||||||
|
// Takes no arguments
|
||||||
|
private static final int MSG_STOP_SEARCH = 2;
|
||||||
|
// Takes no arguments
|
||||||
|
private static final int MSG_ON_CONFIGURATION_CHANGED = 3;
|
||||||
|
|
||||||
|
private static final String KEY_INITIAL_QUERY = "q";
|
||||||
|
private static final String KEY_LAUNCH_ACTIVITY = "a";
|
||||||
|
private static final String KEY_APP_SEARCH_DATA = "d";
|
||||||
|
|
||||||
|
// Context used for getting search UI resources
|
||||||
|
private final Context mContext;
|
||||||
|
|
||||||
|
// Handles messages on the search UI thread.
|
||||||
|
private final SearchDialogHandler mSearchUiThread;
|
||||||
|
|
||||||
|
// The search UI
|
||||||
|
SearchDialog mSearchDialog;
|
||||||
|
|
||||||
|
// If the search UI is visible, this is the callback for the client that showed it.
|
||||||
|
ISearchManagerCallback mCallback = null;
|
||||||
|
|
||||||
|
// Allows disabling of search dialog for stress testing runs
|
||||||
|
private final boolean mDisabledOnBoot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new search dialog wrapper and a search UI thread. The search dialog itself will
|
||||||
|
* be created some asynchronously on the search UI thread.
|
||||||
|
*
|
||||||
|
* @param context Context used for getting search UI resources.
|
||||||
|
*/
|
||||||
|
public SearchDialogWrapper(Context context) {
|
||||||
|
mContext = context;
|
||||||
|
|
||||||
|
mDisabledOnBoot = !TextUtils.isEmpty(SystemProperties.get(DISABLE_SEARCH_PROPERTY));
|
||||||
|
|
||||||
|
// Create the search UI thread
|
||||||
|
HandlerThread t = new HandlerThread(SEARCH_UI_THREAD_NAME, SEARCH_UI_THREAD_PRIORITY);
|
||||||
|
t.start();
|
||||||
|
mSearchUiThread = new SearchDialogHandler(t.getLooper());
|
||||||
|
|
||||||
|
// Create search UI on the search UI thread
|
||||||
|
mSearchUiThread.sendEmptyMessage(MSG_INIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the search UI.
|
||||||
|
* Must be called from the search UI thread.
|
||||||
|
*/
|
||||||
|
private void init() {
|
||||||
|
mSearchDialog = new SearchDialog(mContext);
|
||||||
|
mSearchDialog.setOnCancelListener(this);
|
||||||
|
mSearchDialog.setOnDismissListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerBroadcastReceiver() {
|
||||||
|
IntentFilter closeDialogsFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
|
||||||
|
mContext.registerReceiver(mBroadcastReceiver, closeDialogsFilter);
|
||||||
|
IntentFilter configurationChangedFilter =
|
||||||
|
new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED);
|
||||||
|
mContext.registerReceiver(mBroadcastReceiver, configurationChangedFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unregisterBroadcastReceiver() {
|
||||||
|
mContext.unregisterReceiver(mBroadcastReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the search dialog when requested by the system (e.g. when a phone call comes in).
|
||||||
|
*/
|
||||||
|
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
String action = intent.getAction();
|
||||||
|
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
|
||||||
|
if (DBG) debug(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
|
||||||
|
stopSearch();
|
||||||
|
} else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
|
||||||
|
if (DBG) debug(Intent.ACTION_CONFIGURATION_CHANGED);
|
||||||
|
onConfigurationChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// External API
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launches the search UI.
|
||||||
|
* Can be called from any thread.
|
||||||
|
*
|
||||||
|
* @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()");
|
||||||
|
Message msg = Message.obtain();
|
||||||
|
msg.what = MSG_START_SEARCH;
|
||||||
|
msg.arg1 = selectInitialQuery ? 1 : 0;
|
||||||
|
msg.arg2 = globalSearch ? 1 : 0;
|
||||||
|
msg.obj = searchManagerCallback;
|
||||||
|
Bundle msgData = msg.getData();
|
||||||
|
msgData.putString(KEY_INITIAL_QUERY, initialQuery);
|
||||||
|
msgData.putParcelable(KEY_LAUNCH_ACTIVITY, launchActivity);
|
||||||
|
msgData.putBundle(KEY_APP_SEARCH_DATA, appSearchData);
|
||||||
|
mSearchUiThread.sendMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels the search dialog.
|
||||||
|
* Can be called from any thread.
|
||||||
|
*/
|
||||||
|
public void stopSearch() {
|
||||||
|
if (DBG) debug("stopSearch()");
|
||||||
|
mSearchUiThread.sendEmptyMessage(MSG_STOP_SEARCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the search UI in response to a configuration change.
|
||||||
|
* Can be called from any thread.
|
||||||
|
*/
|
||||||
|
void onConfigurationChanged() {
|
||||||
|
if (DBG) debug("onConfigurationChanged()");
|
||||||
|
mSearchUiThread.sendEmptyMessage(MSG_ON_CONFIGURATION_CHANGED);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Implementation methods that run on the search UI thread
|
||||||
|
//
|
||||||
|
|
||||||
|
private class SearchDialogHandler extends Handler {
|
||||||
|
|
||||||
|
public SearchDialogHandler(Looper looper) {
|
||||||
|
super(looper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
switch (msg.what) {
|
||||||
|
case MSG_INIT:
|
||||||
|
init();
|
||||||
|
break;
|
||||||
|
case MSG_START_SEARCH:
|
||||||
|
handleStartSearchMessage(msg);
|
||||||
|
break;
|
||||||
|
case MSG_STOP_SEARCH:
|
||||||
|
performStopSearch();
|
||||||
|
break;
|
||||||
|
case MSG_ON_CONFIGURATION_CHANGED:
|
||||||
|
performOnConfigurationChanged();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleStartSearchMessage(Message msg) {
|
||||||
|
Bundle msgData = msg.getData();
|
||||||
|
String initialQuery = msgData.getString(KEY_INITIAL_QUERY);
|
||||||
|
boolean selectInitialQuery = msg.arg1 != 0;
|
||||||
|
ComponentName launchActivity =
|
||||||
|
(ComponentName) msgData.getParcelable(KEY_LAUNCH_ACTIVITY);
|
||||||
|
Bundle appSearchData = msgData.getBundle(KEY_APP_SEARCH_DATA);
|
||||||
|
boolean globalSearch = msg.arg2 != 0;
|
||||||
|
ISearchManagerCallback searchManagerCallback = (ISearchManagerCallback) msg.obj;
|
||||||
|
performStartSearch(initialQuery, selectInitialQuery, launchActivity,
|
||||||
|
appSearchData, globalSearch, searchManagerCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actually launches the search UI.
|
||||||
|
* This must be called on the search UI thread.
|
||||||
|
*/
|
||||||
|
void performStartSearch(String initialQuery,
|
||||||
|
boolean selectInitialQuery,
|
||||||
|
ComponentName launchActivity,
|
||||||
|
Bundle appSearchData,
|
||||||
|
boolean globalSearch,
|
||||||
|
ISearchManagerCallback searchManagerCallback) {
|
||||||
|
if (DBG) debug("performStartSearch()");
|
||||||
|
|
||||||
|
if (mDisabledOnBoot) {
|
||||||
|
Log.d(TAG, "ignoring start search request because " + DISABLE_SEARCH_PROPERTY
|
||||||
|
+ " system property is set.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerBroadcastReceiver();
|
||||||
|
mCallback = searchManagerCallback;
|
||||||
|
mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData,
|
||||||
|
globalSearch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actually cancels the search UI.
|
||||||
|
* This must be called on the search UI thread.
|
||||||
|
*/
|
||||||
|
void performStopSearch() {
|
||||||
|
if (DBG) debug("performStopSearch()");
|
||||||
|
mSearchDialog.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must be called from the search UI thread.
|
||||||
|
*/
|
||||||
|
void performOnConfigurationChanged() {
|
||||||
|
if (DBG) debug("performOnConfigurationChanged()");
|
||||||
|
mSearchDialog.onConfigurationChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by {@link SearchDialog} when it goes away.
|
||||||
|
*/
|
||||||
|
public void onDismiss(DialogInterface dialog) {
|
||||||
|
if (DBG) debug("onDismiss()");
|
||||||
|
if (mCallback != null) {
|
||||||
|
try {
|
||||||
|
// should be safe to do on the search UI thread, since it's a oneway interface
|
||||||
|
mCallback.onDismiss();
|
||||||
|
} catch (DeadObjectException ex) {
|
||||||
|
// The process that hosted the callback has died, do nothing
|
||||||
|
} catch (RemoteException ex) {
|
||||||
|
Log.e(TAG, "onDismiss() failed: " + ex);
|
||||||
|
}
|
||||||
|
// we don't need the callback anymore, release it
|
||||||
|
mCallback = null;
|
||||||
|
}
|
||||||
|
unregisterBroadcastReceiver();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by {@link SearchDialog} when the user or activity cancels search.
|
||||||
|
* Whenever this method is called, {@link #onDismiss} is always called afterwards.
|
||||||
|
*/
|
||||||
|
public void onCancel(DialogInterface dialog) {
|
||||||
|
if (DBG) debug("onCancel()");
|
||||||
|
if (mCallback != null) {
|
||||||
|
try {
|
||||||
|
// should be safe to do on the search UI thread, since it's a oneway interface
|
||||||
|
mCallback.onCancel();
|
||||||
|
} catch (DeadObjectException ex) {
|
||||||
|
// The process that hosted the callback has died, do nothing
|
||||||
|
} catch (RemoteException ex) {
|
||||||
|
Log.e(TAG, "onCancel() failed: " + ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void debug(String msg) {
|
||||||
|
Thread thread = Thread.currentThread();
|
||||||
|
Log.d(TAG, msg + " (" + thread.getName() + "-" + thread.getId() + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,52 +18,38 @@ package android.server.search;
|
|||||||
|
|
||||||
import android.app.ISearchManager;
|
import android.app.ISearchManager;
|
||||||
import android.app.ISearchManagerCallback;
|
import android.app.ISearchManagerCallback;
|
||||||
import android.app.SearchDialog;
|
|
||||||
import android.app.SearchManager;
|
import android.app.SearchManager;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.os.SystemProperties;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.List;
|
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
|
* The search manager service handles the search UI, and maintains a registry of searchable
|
||||||
* presentation (UI). Its function is to maintain the map & list of "searchable"
|
* activities.
|
||||||
* items, which provides a mapping from individual activities (where a user might have
|
|
||||||
* invoked search) to specific searchable activities (where the search will be dispatched).
|
|
||||||
*/
|
*/
|
||||||
public class SearchManagerService extends ISearchManager.Stub
|
public class SearchManagerService extends ISearchManager.Stub {
|
||||||
implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener
|
|
||||||
{
|
|
||||||
// general debugging support
|
// general debugging support
|
||||||
private static final String TAG = "SearchManagerService";
|
private static final String TAG = "SearchManagerService";
|
||||||
private static final boolean DBG = false;
|
private static final boolean DBG = false;
|
||||||
|
|
||||||
// class maintenance and general shared data
|
// Context that the service is running in.
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final Handler mHandler;
|
|
||||||
private boolean mSearchablesDirty;
|
|
||||||
private final Searchables mSearchables;
|
|
||||||
|
|
||||||
final SearchDialog mSearchDialog;
|
// This field is initialized in initialize(), and then never modified.
|
||||||
ISearchManagerCallback mCallback = null;
|
// It is volatile since it can be accessed by multiple threads.
|
||||||
|
private volatile Searchables mSearchables;
|
||||||
|
|
||||||
private final boolean mDisabledOnBoot;
|
// This field is initialized in initialize(), and then never modified.
|
||||||
|
// It is volatile since it can be accessed by multiple threads.
|
||||||
private static final String DISABLE_SEARCH_PROPERTY = "dev.disablesearchdialog";
|
private volatile SearchDialogWrapper mSearchDialog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the Search Manager service in the provided system context.
|
* Initializes the Search Manager service in the provided system context.
|
||||||
@@ -73,82 +59,71 @@ public class SearchManagerService extends ISearchManager.Stub
|
|||||||
*/
|
*/
|
||||||
public SearchManagerService(Context context) {
|
public SearchManagerService(Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mHandler = new Handler();
|
// call initialize() after all pending actions on the main system thread have finished
|
||||||
mSearchablesDirty = true;
|
new Handler().post(new Runnable() {
|
||||||
mSearchables = new Searchables(context);
|
public void run() {
|
||||||
mSearchDialog = new SearchDialog(context);
|
initialize();
|
||||||
mSearchDialog.setOnCancelListener(this);
|
}
|
||||||
mSearchDialog.setOnDismissListener(this);
|
});
|
||||||
|
|
||||||
// Setup the infrastructure for updating and maintaining the list
|
|
||||||
// of searchable activities.
|
|
||||||
IntentFilter filter = new IntentFilter();
|
|
||||||
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
|
||||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
|
||||||
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
|
|
||||||
filter.addDataScheme("package");
|
|
||||||
mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
|
|
||||||
|
|
||||||
// After startup settles down, preload the searchables list,
|
|
||||||
// which will reduce the delay when the search UI is invoked.
|
|
||||||
mHandler.post(mRunUpdateSearchable);
|
|
||||||
|
|
||||||
// allows disabling of search dialog for stress testing runs
|
|
||||||
mDisabledOnBoot = !TextUtils.isEmpty(SystemProperties.get(DISABLE_SEARCH_PROPERTY));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listens for intent broadcasts.
|
* Initializes the search UI and the list of searchable activities.
|
||||||
*
|
|
||||||
* The primary purpose here is to refresh the "searchables" list
|
|
||||||
* if packages are added/removed.
|
|
||||||
*/
|
*/
|
||||||
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
|
void initialize() {
|
||||||
|
mSearchables = createSearchables();
|
||||||
|
mSearchDialog = new SearchDialogWrapper(mContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Searchables createSearchables() {
|
||||||
|
Searchables searchables = new Searchables(mContext);
|
||||||
|
searchables.buildSearchableList();
|
||||||
|
|
||||||
|
IntentFilter packageFilter = new IntentFilter();
|
||||||
|
packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||||
|
packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||||
|
packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
|
||||||
|
packageFilter.addDataScheme("package");
|
||||||
|
mContext.registerReceiver(mPackageChangedReceiver, packageFilter);
|
||||||
|
|
||||||
|
return searchables;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the "searchables" list when packages are added/removed.
|
||||||
|
*/
|
||||||
|
private BroadcastReceiver mPackageChangedReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
|
|
||||||
// First, test for intents that matter at any time
|
if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
|
||||||
if (action.equals(Intent.ACTION_PACKAGE_ADDED) ||
|
Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
|
||||||
action.equals(Intent.ACTION_PACKAGE_REMOVED) ||
|
Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
|
||||||
action.equals(Intent.ACTION_PACKAGE_CHANGED)) {
|
if (DBG) Log.d(TAG, "Got " + action);
|
||||||
mSearchablesDirty = true;
|
// Dismiss search dialog, since the search context may no longer be valid
|
||||||
mHandler.post(mRunUpdateSearchable);
|
mSearchDialog.stopSearch();
|
||||||
return;
|
// Update list of searchable activities
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This runnable (for the main handler / UI thread) will update the searchables list.
|
|
||||||
*/
|
|
||||||
private Runnable mRunUpdateSearchable = new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
updateSearchablesIfDirty();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the list of searchables, either at startup or in response to
|
|
||||||
* a package add/remove broadcast message.
|
|
||||||
*/
|
|
||||||
private void updateSearchables() {
|
|
||||||
if (DBG) debug("updateSearchables()");
|
|
||||||
mSearchables.buildSearchableList();
|
mSearchables.buildSearchableList();
|
||||||
mSearchablesDirty = false;
|
broadcastSearchablesChanged();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the list of searchables if needed.
|
* Informs all listeners that the list of searchables has been updated.
|
||||||
*/
|
*/
|
||||||
private void updateSearchablesIfDirty() {
|
void broadcastSearchablesChanged() {
|
||||||
if (mSearchablesDirty) {
|
mContext.sendBroadcast(
|
||||||
updateSearchables();
|
new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Searchable activities API
|
||||||
|
//
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the SearchableInfo for a given activity
|
* Returns the SearchableInfo for a given activity.
|
||||||
*
|
*
|
||||||
* @param launchActivity The activity from which we're launching this search.
|
* @param launchActivity The activity from which we're launching this search.
|
||||||
* @param globalSearch If false, this will only launch the search that has been specifically
|
* @param globalSearch If false, this will only launch the search that has been specifically
|
||||||
@@ -158,226 +133,84 @@ public class SearchManagerService extends ISearchManager.Stub
|
|||||||
* @return Returns a SearchableInfo record describing the parameters of the search,
|
* @return Returns a SearchableInfo record describing the parameters of the search,
|
||||||
* or null if no searchable metadata was available.
|
* or null if no searchable metadata was available.
|
||||||
*/
|
*/
|
||||||
public SearchableInfo getSearchableInfo(ComponentName launchActivity, boolean globalSearch) {
|
public SearchableInfo getSearchableInfo(final ComponentName launchActivity,
|
||||||
updateSearchablesIfDirty();
|
final boolean globalSearch) {
|
||||||
SearchableInfo si = null;
|
if (mSearchables == null) return null;
|
||||||
if (globalSearch) {
|
if (globalSearch) {
|
||||||
si = mSearchables.getDefaultSearchable();
|
return mSearchables.getDefaultSearchable();
|
||||||
} else {
|
} else {
|
||||||
if (launchActivity == null) {
|
if (launchActivity == null) {
|
||||||
Log.e(TAG, "getSearchableInfo(), activity == null");
|
Log.e(TAG, "getSearchableInfo(), activity == null");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
si = mSearchables.getSearchableInfo(launchActivity);
|
return mSearchables.getSearchableInfo(launchActivity);
|
||||||
}
|
}
|
||||||
|
|
||||||
return si;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of the searchable activities that can be included in global search.
|
* Returns a list of the searchable activities that can be included in global search.
|
||||||
*/
|
*/
|
||||||
public List<SearchableInfo> getSearchablesInGlobalSearch() {
|
public List<SearchableInfo> getSearchablesInGlobalSearch() {
|
||||||
updateSearchablesIfDirty();
|
if (mSearchables == null) return null;
|
||||||
return mSearchables.getSearchablesInGlobalSearchList();
|
return mSearchables.getSearchablesInGlobalSearchList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Launches the search UI on the main thread of the service.
|
* Returns a list of the searchable activities that handle web searches.
|
||||||
|
* Can be called from any thread.
|
||||||
|
*/
|
||||||
|
public List<SearchableInfo> getSearchablesForWebSearch() {
|
||||||
|
if (mSearchables == null) return null;
|
||||||
|
return mSearchables.getSearchablesForWebSearchList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default searchable activity for web searches.
|
||||||
|
* Can be called from any thread.
|
||||||
|
*/
|
||||||
|
public SearchableInfo getDefaultSearchableForWebSearch() {
|
||||||
|
if (mSearchables == null) return null;
|
||||||
|
return mSearchables.getDefaultSearchableForWebSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default searchable activity for web searches.
|
||||||
|
* Can be called from any thread.
|
||||||
|
*/
|
||||||
|
public void setDefaultWebSearch(final ComponentName component) {
|
||||||
|
if (mSearchables == null) return;
|
||||||
|
mSearchables.setDefaultWebSearch(component);
|
||||||
|
broadcastSearchablesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search UI API
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launches the search UI. Can be called from any thread.
|
||||||
*
|
*
|
||||||
* @see SearchManager#startSearch(String, boolean, ComponentName, Bundle, boolean)
|
* @see SearchManager#startSearch(String, boolean, ComponentName, Bundle, boolean)
|
||||||
*/
|
*/
|
||||||
public void startSearch(final String initialQuery,
|
public void startSearch(String initialQuery,
|
||||||
final boolean selectInitialQuery,
|
boolean selectInitialQuery,
|
||||||
final ComponentName launchActivity,
|
ComponentName launchActivity,
|
||||||
final Bundle appSearchData,
|
Bundle appSearchData,
|
||||||
final boolean globalSearch,
|
boolean globalSearch,
|
||||||
final ISearchManagerCallback searchManagerCallback) {
|
ISearchManagerCallback searchManagerCallback) {
|
||||||
if (DBG) debug("startSearch()");
|
if (mSearchDialog == null) return;
|
||||||
Runnable task = new Runnable() {
|
mSearchDialog.startSearch(initialQuery,
|
||||||
public void run() {
|
|
||||||
performStartSearch(initialQuery,
|
|
||||||
selectInitialQuery,
|
selectInitialQuery,
|
||||||
launchActivity,
|
launchActivity,
|
||||||
appSearchData,
|
appSearchData,
|
||||||
globalSearch,
|
globalSearch,
|
||||||
searchManagerCallback);
|
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()");
|
|
||||||
|
|
||||||
if (mDisabledOnBoot) {
|
|
||||||
Log.d(TAG, "ignoring start search request because " + DISABLE_SEARCH_PROPERTY
|
|
||||||
+ " system property is set.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData,
|
|
||||||
globalSearch);
|
|
||||||
if (searchManagerCallback != null) {
|
|
||||||
mCallback = searchManagerCallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels the search dialog. Can be called from any thread.
|
* Cancels the search dialog. Can be called from any thread.
|
||||||
*/
|
*/
|
||||||
public void stopSearch() {
|
public void stopSearch() {
|
||||||
if (DBG) debug("stopSearch()");
|
if (mSearchDialog == null) return;
|
||||||
mHandler.post(new Runnable() {
|
mSearchDialog.stopSearch();
|
||||||
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.
|
|
||||||
*/
|
|
||||||
public List<SearchableInfo> getSearchablesForWebSearch() {
|
|
||||||
updateSearchablesIfDirty();
|
|
||||||
return mSearchables.getSearchablesForWebSearchList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the default searchable activity for web searches.
|
|
||||||
*/
|
|
||||||
public SearchableInfo getDefaultSearchableForWebSearch() {
|
|
||||||
updateSearchablesIfDirty();
|
|
||||||
return mSearchables.getDefaultSearchableForWebSearch();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the default searchable activity for web searches.
|
|
||||||
*/
|
|
||||||
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() + ")");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
package android.server.search;
|
package android.server.search;
|
||||||
|
|
||||||
import com.android.internal.app.ResolverActivity;
|
import com.android.internal.app.ResolverActivity;
|
||||||
import com.android.internal.R;
|
|
||||||
|
|
||||||
import android.app.SearchManager;
|
import android.app.SearchManager;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
@@ -27,7 +26,6 @@ import android.content.IntentFilter;
|
|||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -264,7 +262,7 @@ public class Searchables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find the default web search provider.
|
// Find the default web search provider.
|
||||||
ComponentName webSearchActivity = getPreferredWebSearchActivity();
|
ComponentName webSearchActivity = getPreferredWebSearchActivity(mContext);
|
||||||
SearchableInfo newDefaultSearchableForWebSearch = null;
|
SearchableInfo newDefaultSearchableForWebSearch = null;
|
||||||
if (webSearchActivity != null) {
|
if (webSearchActivity != null) {
|
||||||
newDefaultSearchableForWebSearch = newSearchablesMap.get(webSearchActivity);
|
newDefaultSearchableForWebSearch = newSearchablesMap.get(webSearchActivity);
|
||||||
@@ -283,9 +281,6 @@ public class Searchables {
|
|||||||
mDefaultSearchable = newDefaultSearchable;
|
mDefaultSearchable = newDefaultSearchable;
|
||||||
mDefaultSearchableForWebSearch = newDefaultSearchableForWebSearch;
|
mDefaultSearchableForWebSearch = newDefaultSearchableForWebSearch;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inform all listeners that the list of searchables has been updated.
|
|
||||||
mContext.sendBroadcast(new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -295,9 +290,10 @@ public class Searchables {
|
|||||||
* @param action Intent action for which this activity is to be set as preferred.
|
* @param action Intent action for which this activity is to be set as preferred.
|
||||||
* @return true if component was detected and set as preferred activity, false if not.
|
* @return true if component was detected and set as preferred activity, false if not.
|
||||||
*/
|
*/
|
||||||
private boolean setPreferredActivity(ComponentName component, String action) {
|
private static boolean setPreferredActivity(Context context,
|
||||||
|
ComponentName component, String action) {
|
||||||
Log.d(LOG_TAG, "Checking component " + component);
|
Log.d(LOG_TAG, "Checking component " + component);
|
||||||
PackageManager pm = mContext.getPackageManager();
|
PackageManager pm = context.getPackageManager();
|
||||||
ActivityInfo ai;
|
ActivityInfo ai;
|
||||||
try {
|
try {
|
||||||
ai = pm.getActivityInfo(component, 0);
|
ai = pm.getActivityInfo(component, 0);
|
||||||
@@ -326,10 +322,10 @@ public class Searchables {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ComponentName getPreferredWebSearchActivity() {
|
private static ComponentName getPreferredWebSearchActivity(Context context) {
|
||||||
// Check if we have a preferred web search activity.
|
// Check if we have a preferred web search activity.
|
||||||
Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
|
Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
|
||||||
PackageManager pm = mContext.getPackageManager();
|
PackageManager pm = context.getPackageManager();
|
||||||
ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
|
ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||||
|
|
||||||
if (ri == null || ri.activityInfo.name.equals(ResolverActivity.class.getName())) {
|
if (ri == null || ri.activityInfo.name.equals(ResolverActivity.class.getName())) {
|
||||||
@@ -338,11 +334,11 @@ public class Searchables {
|
|||||||
// The components in the providers array are checked in the order of declaration so the
|
// The components in the providers array are checked in the order of declaration so the
|
||||||
// first one has the highest priority. If the component exists in the system it is set
|
// first one has the highest priority. If the component exists in the system it is set
|
||||||
// as the preferred activity to handle intent action web search.
|
// as the preferred activity to handle intent action web search.
|
||||||
String[] preferredActivities = mContext.getResources().getStringArray(
|
String[] preferredActivities = context.getResources().getStringArray(
|
||||||
com.android.internal.R.array.default_web_search_providers);
|
com.android.internal.R.array.default_web_search_providers);
|
||||||
for (String componentName : preferredActivities) {
|
for (String componentName : preferredActivities) {
|
||||||
ComponentName component = ComponentName.unflattenFromString(componentName);
|
ComponentName component = ComponentName.unflattenFromString(componentName);
|
||||||
if (setPreferredActivity(component, Intent.ACTION_WEB_SEARCH)) {
|
if (setPreferredActivity(context, component, Intent.ACTION_WEB_SEARCH)) {
|
||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -354,7 +350,8 @@ public class Searchables {
|
|||||||
if (cn.flattenToShortString().equals(GOOGLE_SEARCH_COMPONENT_NAME)) {
|
if (cn.flattenToShortString().equals(GOOGLE_SEARCH_COMPONENT_NAME)) {
|
||||||
ComponentName enhancedGoogleSearch = ComponentName.unflattenFromString(
|
ComponentName enhancedGoogleSearch = ComponentName.unflattenFromString(
|
||||||
ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME);
|
ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME);
|
||||||
if (setPreferredActivity(enhancedGoogleSearch, Intent.ACTION_WEB_SEARCH)) {
|
if (setPreferredActivity(context, enhancedGoogleSearch,
|
||||||
|
Intent.ACTION_WEB_SEARCH)) {
|
||||||
return enhancedGoogleSearch;
|
return enhancedGoogleSearch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -397,7 +394,7 @@ public class Searchables {
|
|||||||
* Sets the default searchable activity for web searches.
|
* Sets the default searchable activity for web searches.
|
||||||
*/
|
*/
|
||||||
public synchronized void setDefaultWebSearch(ComponentName component) {
|
public synchronized void setDefaultWebSearch(ComponentName component) {
|
||||||
setPreferredActivity(component, Intent.ACTION_WEB_SEARCH);
|
setPreferredActivity(mContext, component, Intent.ACTION_WEB_SEARCH);
|
||||||
buildSearchableList();
|
buildSearchableList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,8 +107,6 @@ public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalAct
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Checks that the search UI is not visible.
|
// 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() {
|
private void assertSearchNotVisible() {
|
||||||
SearchManager searchManager = (SearchManager)
|
SearchManager searchManager = (SearchManager)
|
||||||
mContext.getSystemService(Context.SEARCH_SERVICE);
|
mContext.getSystemService(Context.SEARCH_SERVICE);
|
||||||
@@ -245,22 +243,4 @@ public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalAct
|
|||||||
assertSearchNotVisible();
|
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user