Merge "Unhide new SoundPool API's. Bug 2415373."
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index ed38240..4598bb5 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -822,11 +822,6 @@
final SearchManager searchManager = (SearchManager) mContext
.getSystemService(Context.SEARCH_SERVICE);
- // can't start search without an associated activity (e.g a system dialog)
- if (!searchManager.hasIdent()) {
- return false;
- }
-
// associate search with owner activity if possible (otherwise it will default to
// global search).
final ComponentName appName = getAssociatedActivity();
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index e4c1ba6..581b436 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -16,6 +16,9 @@
package android.app;
+import com.android.common.Patterns;
+import com.android.common.speech.Recognition;
+
import static android.app.SuggestionsAdapter.getColumnString;
import android.content.ActivityNotFoundException;
@@ -67,9 +70,6 @@
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
-import com.android.common.Patterns;
-import com.android.common.speech.Recognition;
-
import java.util.ArrayList;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicLong;
@@ -89,10 +89,7 @@
private static final String INSTANCE_KEY_COMPONENT = "comp";
private static final String INSTANCE_KEY_APPDATA = "data";
- private static final String INSTANCE_KEY_GLOBALSEARCH = "glob";
- private static final String INSTANCE_KEY_STORED_COMPONENT = "sComp";
private static final String INSTANCE_KEY_STORED_APPDATA = "sData";
- private static final String INSTANCE_KEY_PREVIOUS_COMPONENTS = "sPrev";
private static final String INSTANCE_KEY_USER_QUERY = "uQry";
// The string used for privateImeOptions to identify to the IME that it should not show
@@ -115,19 +112,8 @@
private SearchableInfo mSearchable;
private ComponentName mLaunchComponent;
private Bundle mAppSearchData;
- private boolean mGlobalSearchMode;
private Context mActivityContext;
private SearchManager mSearchManager;
-
- // Values we store to allow user to toggle between in-app search and global search.
- private ComponentName mStoredComponentName;
- private Bundle mStoredAppSearchData;
-
- // stack of previous searchables, to support the BACK key after
- // SearchManager.INTENT_ACTION_CHANGE_SEARCH_SOURCE.
- // The top of the stack (= previous searchable) is the last element of the list,
- // since adding and removing is efficient at the end of an ArrayList.
- private ArrayList<ComponentName> mPreviousComponents;
// For voice searching
private final Intent mVoiceWebSearchIntent;
@@ -158,7 +144,7 @@
* @param context Application Context we can use for system acess
*/
public SearchDialog(Context context, SearchManager searchManager) {
- super(context, com.android.internal.R.style.Theme_GlobalSearchBar);
+ super(context, com.android.internal.R.style.Theme_SearchBar);
// Save voice intent for later queries/launching
mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
@@ -241,14 +227,8 @@
* @return true if search dialog launched, false if not
*/
public boolean show(String initialQuery, boolean selectInitialQuery,
- ComponentName componentName, Bundle appSearchData, boolean globalSearch) {
-
- // Reset any stored values from last time dialog was shown.
- mStoredComponentName = null;
- mStoredAppSearchData = null;
-
- boolean success = doShow(initialQuery, selectInitialQuery, componentName, appSearchData,
- globalSearch);
+ ComponentName componentName, Bundle appSearchData) {
+ boolean success = doShow(initialQuery, selectInitialQuery, componentName, appSearchData);
if (success) {
// Display the drop down as soon as possible instead of waiting for the rest of the
// pending UI stuff to get done, so that things appear faster to the user.
@@ -257,67 +237,16 @@
return success;
}
- private boolean isInRealAppSearch() {
- return !mGlobalSearchMode
- && (mPreviousComponents == null || mPreviousComponents.isEmpty());
- }
-
/**
- * Called in response to a press of the hard search button in
- * {@link #onKeyDown(int, KeyEvent)}, this method toggles between in-app
- * search and global search when relevant.
- *
- * If pressed within an in-app search context, this switches the search dialog out to
- * global search. If pressed within a global search context that was originally an in-app
- * search context, this switches back to the in-app search context. If pressed within a
- * global search context that has no original in-app search context (e.g., global search
- * from Home), this does nothing.
- *
- * @return false if we wanted to toggle context but could not do so successfully, true
- * in all other cases
- */
- private boolean toggleGlobalSearch() {
- String currentSearchText = mSearchAutoComplete.getText().toString();
- if (!mGlobalSearchMode) {
- mStoredComponentName = mLaunchComponent;
- mStoredAppSearchData = mAppSearchData;
-
- // If this is the browser, we have a special case to not show the icon to the left
- // of the text field, for extra space for url entry (this should be reconciled in
- // Eclair). So special case a second tap of the search button to remove any
- // already-entered text so that we can be sure to show the "Quick Search Box" hint
- // text to still make it clear to the user that we've jumped out to global search.
- //
- // TODO: When the browser icon issue is reconciled in Eclair, remove this special case.
- if (isBrowserSearch()) currentSearchText = "";
-
- cancel();
- mSearchManager.startGlobalSearch(currentSearchText, false, mStoredAppSearchData);
- return true;
- } else {
- if (mStoredComponentName != null) {
- // This means we should toggle *back* to an in-app search context from
- // global search.
- return doShow(currentSearchText, false, mStoredComponentName,
- mStoredAppSearchData, false);
- } else {
- return true;
- }
- }
- }
-
- /**
- * Does the rest of the work required to show the search dialog. Called by both
- * {@link #show(String, boolean, ComponentName, Bundle, boolean)} and
- * {@link #toggleGlobalSearch()}.
- *
+ * Does the rest of the work required to show the search dialog. Called by
+ * {@link #show(String, boolean, ComponentName, Bundle)} and
+ *
* @return true if search dialog showed, false if not
*/
private boolean doShow(String initialQuery, boolean selectInitialQuery,
- ComponentName componentName, Bundle appSearchData,
- boolean globalSearch) {
+ ComponentName componentName, Bundle appSearchData) {
// set up the searchable and show the dialog
- if (!show(componentName, appSearchData, globalSearch)) {
+ if (!show(componentName, appSearchData)) {
return false;
}
@@ -335,38 +264,24 @@
*
* @return <code>true</code> if search dialog launched
*/
- private boolean show(ComponentName componentName, Bundle appSearchData,
- boolean globalSearch) {
+ private boolean show(ComponentName componentName, Bundle appSearchData) {
if (DBG) {
Log.d(LOG_TAG, "show(" + componentName + ", "
- + appSearchData + ", " + globalSearch + ")");
+ + appSearchData + ")");
}
SearchManager searchManager = (SearchManager)
mContext.getSystemService(Context.SEARCH_SERVICE);
- // Try to get the searchable info for the provided component (or for global search,
- // if globalSearch == true).
- mSearchable = searchManager.getSearchableInfo(componentName, globalSearch);
-
- // If we got back nothing, and it wasn't a request for global search, then try again
- // for global search, as we'll try to launch that in lieu of any component-specific search.
- if (!globalSearch && mSearchable == null) {
- globalSearch = true;
- mSearchable = searchManager.getSearchableInfo(componentName, globalSearch);
- }
+ // Try to get the searchable info for the provided component.
+ mSearchable = searchManager.getSearchableInfo(componentName, false);
- // If there's not even a searchable info available for global search, then really give up.
if (mSearchable == null) {
- Log.w(LOG_TAG, "No global search provider.");
return false;
}
mLaunchComponent = componentName;
mAppSearchData = appSearchData;
- // Using globalSearch here is just an optimization, just calling
- // isDefaultSearchable() should always give the same result.
- mGlobalSearchMode = globalSearch || searchManager.isDefaultSearchable(mSearchable);
mActivityContext = mSearchable.getActivityContext(getContext());
// show the dialog. this will call onStart().
@@ -375,20 +290,6 @@
// of any bad state in the AutoCompleteTextView etc
createContentView();
- // The Dialog uses a ContextThemeWrapper for the context; use this to change the
- // theme out from underneath us, between the global search theme and the in-app
- // search theme. They are identical except that the global search theme does not
- // dim the background of the window (because global search is full screen so it's
- // not needed and this should save a little bit of time on global search invocation).
- Object context = getContext();
- if (context instanceof ContextThemeWrapper) {
- ContextThemeWrapper wrapper = (ContextThemeWrapper) context;
- if (globalSearch) {
- wrapper.setTheme(com.android.internal.R.style.Theme_GlobalSearchBar);
- } else {
- wrapper.setTheme(com.android.internal.R.style.Theme_SearchBar);
- }
- }
show();
}
updateUI();
@@ -414,7 +315,6 @@
mSearchable = null;
mActivityContext = null;
mUserQuery = null;
- mPreviousComponents = null;
}
/**
@@ -428,7 +328,7 @@
mWorkingSpinner.setVisible(working, false);
mWorkingSpinner.invalidateSelf();
}
-
+
/**
* Closes and gets rid of the suggestions adapter.
*/
@@ -442,7 +342,7 @@
}
mSuggestionsAdapter = null;
}
-
+
/**
* Save the minimal set of data necessary to recreate the search
*
@@ -458,10 +358,6 @@
// setup info so I can recreate this particular search
bundle.putParcelable(INSTANCE_KEY_COMPONENT, mLaunchComponent);
bundle.putBundle(INSTANCE_KEY_APPDATA, mAppSearchData);
- bundle.putBoolean(INSTANCE_KEY_GLOBALSEARCH, mGlobalSearchMode);
- bundle.putParcelable(INSTANCE_KEY_STORED_COMPONENT, mStoredComponentName);
- bundle.putBundle(INSTANCE_KEY_STORED_APPDATA, mStoredAppSearchData);
- bundle.putParcelableArrayList(INSTANCE_KEY_PREVIOUS_COMPONENTS, mPreviousComponents);
bundle.putString(INSTANCE_KEY_USER_QUERY, mUserQuery);
return bundle;
@@ -481,22 +377,10 @@
ComponentName launchComponent = savedInstanceState.getParcelable(INSTANCE_KEY_COMPONENT);
Bundle appSearchData = savedInstanceState.getBundle(INSTANCE_KEY_APPDATA);
- boolean globalSearch = savedInstanceState.getBoolean(INSTANCE_KEY_GLOBALSEARCH);
- ComponentName storedComponentName =
- savedInstanceState.getParcelable(INSTANCE_KEY_STORED_COMPONENT);
- Bundle storedAppSearchData =
- savedInstanceState.getBundle(INSTANCE_KEY_STORED_APPDATA);
- ArrayList<ComponentName> previousComponents =
- savedInstanceState.getParcelableArrayList(INSTANCE_KEY_PREVIOUS_COMPONENTS);
String userQuery = savedInstanceState.getString(INSTANCE_KEY_USER_QUERY);
- // Set stored state
- mStoredComponentName = storedComponentName;
- mStoredAppSearchData = storedAppSearchData;
- mPreviousComponents = previousComponents;
-
// show the dialog.
- if (!doShow(userQuery, false, launchComponent, appSearchData, globalSearch)) {
+ if (!doShow(userQuery, false, launchComponent, appSearchData)) {
// for some reason, we couldn't re-instantiate
return;
}
@@ -506,7 +390,7 @@
* Called after resources have changed, e.g. after screen rotation or locale change.
*/
public void onConfigurationChanged() {
- if (isShowing()) {
+ if (mSearchable != null && isShowing()) {
// Redraw (resources may have changed)
updateSearchButton();
updateSearchAppIcon();
@@ -571,19 +455,13 @@
// we dismiss the entire dialog instead
mSearchAutoComplete.setDropDownDismissedOnCompletion(false);
- if (!isInRealAppSearch()) {
- mSearchAutoComplete.setDropDownAlwaysVisible(true); // fill space until results come in
- } else {
- mSearchAutoComplete.setDropDownAlwaysVisible(false);
- }
-
mSearchAutoComplete.setForceIgnoreOutsideTouch(true);
// attach the suggestions adapter, if suggestions are available
// The existence of a suggestions authority is the proxy for "suggestions available here"
if (mSearchable.getSuggestAuthority() != null) {
mSuggestionsAdapter = new SuggestionsAdapter(getContext(), this, mSearchable,
- mOutsideDrawablesCache, mGlobalSearchMode);
+ mOutsideDrawablesCache);
mSearchAutoComplete.setAdapter(mSuggestionsAdapter);
}
}
@@ -611,7 +489,7 @@
// global search, for extra space for url entry.
//
// TODO: Remove this special case once the issue has been reconciled in Eclair.
- if (mGlobalSearchMode || isBrowserSearch()) {
+ if (isBrowserSearch()) {
mAppIcon.setImageResource(0);
mAppIcon.setVisibility(View.GONE);
mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_GLOBAL,
@@ -705,15 +583,13 @@
/**
* Hack to determine whether this is the browser, so we can remove the browser icon
- * to the left of the search field, as a special requirement for Donut.
- *
- * TODO: For Eclair, reconcile this with the rest of the global search UI.
+ * to the left of the search field.
*/
private boolean isBrowserSearch() {
return mLaunchComponent.flattenToShortString().startsWith("com.android.browser/");
}
- /**
+ /*
* Listeners of various types
*/
@@ -769,7 +645,7 @@
return super.onKeyDown(keyCode, event);
}
-
+
/**
* Callback to watch the textedit field for empty/non-empty
*/
@@ -799,12 +675,8 @@
if (mSearchable.autoUrlDetect() && !mSearchAutoComplete.isPerformingCompletion()) {
// The user changed the query, check if it is a URL and if so change the search
// button in the soft keyboard to the 'Go' button.
- int options = (mSearchAutoComplete.getImeOptions() & (~EditorInfo.IME_MASK_ACTION));
- if (Patterns.WEB_URL.matcher(mUserQuery).matches()) {
- options = options | EditorInfo.IME_ACTION_GO;
- } else {
- options = options | EditorInfo.IME_ACTION_SEARCH;
- }
+ int options = (mSearchAutoComplete.getImeOptions() & (~EditorInfo.IME_MASK_ACTION))
+ | EditorInfo.IME_ACTION_GO;
if (options != mSearchAutoCompleteImeOptions) {
mSearchAutoCompleteImeOptions = options;
mSearchAutoComplete.setImeOptions(options);
@@ -881,7 +753,8 @@
if (searchable.getVoiceSearchLaunchWebSearch()) {
getContext().startActivity(mVoiceWebSearchIntent);
} else if (searchable.getVoiceSearchLaunchRecognizer()) {
- Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent);
+ Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent,
+ searchable);
getContext().startActivity(appSearchIntent);
}
} catch (ActivityNotFoundException e) {
@@ -899,8 +772,8 @@
* @param baseIntent The voice app search intent to start from
* @return A completely-configured intent ready to send to the voice search activity
*/
- private Intent createVoiceAppSearchIntent(Intent baseIntent) {
- ComponentName searchActivity = mSearchable.getSearchActivity();
+ private Intent createVoiceAppSearchIntent(Intent baseIntent, SearchableInfo searchable) {
+ ComponentName searchActivity = searchable.getSearchActivity();
// create the necessary intent to set up a search-and-forward operation
// in the voice search system. We have to keep the bundle separate,
@@ -930,17 +803,17 @@
String language = null;
int maxResults = 1;
Resources resources = mActivityContext.getResources();
- if (mSearchable.getVoiceLanguageModeId() != 0) {
- languageModel = resources.getString(mSearchable.getVoiceLanguageModeId());
+ if (searchable.getVoiceLanguageModeId() != 0) {
+ languageModel = resources.getString(searchable.getVoiceLanguageModeId());
}
- if (mSearchable.getVoicePromptTextId() != 0) {
- prompt = resources.getString(mSearchable.getVoicePromptTextId());
+ if (searchable.getVoicePromptTextId() != 0) {
+ prompt = resources.getString(searchable.getVoicePromptTextId());
}
- if (mSearchable.getVoiceLanguageId() != 0) {
- language = resources.getString(mSearchable.getVoiceLanguageId());
+ if (searchable.getVoiceLanguageId() != 0) {
+ language = resources.getString(searchable.getVoiceLanguageId());
}
- if (mSearchable.getVoiceMaxResults() != 0) {
- maxResults = mSearchable.getVoiceMaxResults();
+ if (searchable.getVoiceMaxResults() != 0) {
+ maxResults = searchable.getVoiceMaxResults();
}
voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel);
voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
@@ -1140,14 +1013,9 @@
*/
protected void launchQuerySearch(int actionKey, String actionMsg) {
String query = mSearchAutoComplete.getText().toString();
- String action = mGlobalSearchMode ? Intent.ACTION_WEB_SEARCH : Intent.ACTION_SEARCH;
+ String action = Intent.ACTION_SEARCH;
Intent intent = createIntent(action, null, null, query, null,
- actionKey, actionMsg, null);
- // Allow GlobalSearch to log and create shortcut for searches launched by
- // the search button, enter key or an action key.
- if (mGlobalSearchMode) {
- mSuggestionsAdapter.reportSearch(query);
- }
+ actionKey, actionMsg);
launchIntent(intent);
}
@@ -1160,7 +1028,7 @@
protected boolean launchSuggestion(int position) {
return launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null);
}
-
+
/**
* Launches an intent based on a suggestion.
*
@@ -1177,20 +1045,7 @@
Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg);
- // report back about the click
- if (mGlobalSearchMode) {
- // in global search mode, do it via cursor
- mSuggestionsAdapter.callCursorOnClick(c, position, actionKey, actionMsg);
- } else if (intent != null
- && mPreviousComponents != null
- && !mPreviousComponents.isEmpty()) {
- // in-app search (and we have pivoted in as told by mPreviousComponents,
- // which is used for keeping track of what we pop back to when we are pivoting into
- // in app search.)
- reportInAppClickToGlobalSearch(c, intent);
- }
-
- // launch the intent
+ // launch the intent
launchIntent(intent);
return true;
@@ -1199,163 +1054,28 @@
}
/**
- * Report a click from an in app search result back to global search for shortcutting porpoises.
- *
- * @param c The cursor that is pointing to the clicked position.
- * @param intent The intent that will be launched for the click.
- */
- private void reportInAppClickToGlobalSearch(Cursor c, Intent intent) {
- // for in app search, still tell global search via content provider
- Uri uri = getClickReportingUri();
- final ContentValues cv = new ContentValues();
- cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_QUERY, mUserQuery);
- final ComponentName source = mSearchable.getSearchActivity();
- cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_COMPONENT, source.flattenToShortString());
-
- // grab the intent columns from the intent we created since it has additional
- // logic for falling back on the searchable default
- cv.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION, intent.getAction());
- cv.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, intent.getDataString());
- cv.put(SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME,
- intent.getComponent().flattenToShortString());
-
- // ensure the icons will work for global search
- cv.put(SearchManager.SUGGEST_COLUMN_ICON_1,
- wrapIconForPackage(
- mSearchable.getSuggestPackage(),
- getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_1)));
- cv.put(SearchManager.SUGGEST_COLUMN_ICON_2,
- wrapIconForPackage(
- mSearchable.getSuggestPackage(),
- getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_2)));
-
- // the rest can be passed through directly
- cv.put(SearchManager.SUGGEST_COLUMN_FORMAT,
- getColumnString(c, SearchManager.SUGGEST_COLUMN_FORMAT));
- cv.put(SearchManager.SUGGEST_COLUMN_TEXT_1,
- getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_1));
- cv.put(SearchManager.SUGGEST_COLUMN_TEXT_2,
- getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_2));
- cv.put(SearchManager.SUGGEST_COLUMN_QUERY,
- getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY));
- cv.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
- getColumnString(c, SearchManager.SUGGEST_COLUMN_SHORTCUT_ID));
- // note: deliberately omitting background color since it is only for global search
- // "more results" entries
- mContext.getContentResolver().insert(uri, cv);
- }
-
- /**
- * @return A URI appropriate for reporting a click.
- */
- private Uri getClickReportingUri() {
- Uri.Builder uriBuilder = new Uri.Builder()
- .scheme(ContentResolver.SCHEME_CONTENT)
- .authority(SearchManager.SEARCH_CLICK_REPORT_AUTHORITY);
-
- uriBuilder.appendPath(SearchManager.SEARCH_CLICK_REPORT_URI_PATH);
-
- return uriBuilder
- .query("") // TODO: Remove, workaround for a bug in Uri.writeToParcel()
- .fragment("") // TODO: Remove, workaround for a bug in Uri.writeToParcel()
- .build();
- }
-
- /**
- * Wraps an icon for a particular package. If the icon is a resource id, it is converted into
- * an android.resource:// URI.
- *
- * @param packageName The source of the icon
- * @param icon The icon retrieved from a suggestion column
- * @return An icon string appropriate for the package.
- */
- private String wrapIconForPackage(String packageName, String icon) {
- if (icon == null || icon.length() == 0 || "0".equals(icon)) {
- // SearchManager specifies that null or zero can be returned to indicate
- // no icon. We also allow empty string.
- return null;
- } else if (!Character.isDigit(icon.charAt(0))){
- return icon;
- } else {
- return new Uri.Builder()
- .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
- .authority(packageName)
- .encodedPath(icon)
- .toString();
- }
- }
-
- /**
* Launches an intent, including any special intent handling.
*/
private void launchIntent(Intent intent) {
if (intent == null) {
return;
}
- if (handleSpecialIntent(intent)){
- return;
- }
Log.d(LOG_TAG, "launching " + intent);
try {
- // in global search mode, we send the activity straight to the original suggestion
- // source. this is because GlobalSearch may not have permission to launch the
- // intent, and to avoid the extra step of going through GlobalSearch.
- if (mGlobalSearchMode) {
- launchGlobalSearchIntent(intent);
- if (mStoredComponentName != null) {
- // If we're embedded in an application, dismiss the dialog.
- // This ensures that if the intent is handled by the current
- // activity, it's not obscured by the dialog.
- dismiss();
- }
- } else {
- // If the intent was created from a suggestion, it will always have an explicit
- // component here.
- Log.i(LOG_TAG, "Starting (as ourselves) " + intent.toURI());
- getContext().startActivity(intent);
- // If the search switches to a different activity,
- // SearchDialogWrapper#performActivityResuming
- // will handle hiding the dialog when the next activity starts, but for
- // real in-app search, we still need to dismiss the dialog.
- if (isInRealAppSearch()) {
- dismiss();
- }
- }
+ // If the intent was created from a suggestion, it will always have an explicit
+ // component here.
+ Log.i(LOG_TAG, "Starting (as ourselves) " + intent.toURI());
+ getContext().startActivity(intent);
+ // If the search switches to a different activity,
+ // SearchDialogWrapper#performActivityResuming
+ // will handle hiding the dialog when the next activity starts, but for
+ // real in-app search, we still need to dismiss the dialog.
+ dismiss();
} catch (RuntimeException ex) {
Log.e(LOG_TAG, "Failed launch activity: " + intent, ex);
}
}
- private void launchGlobalSearchIntent(Intent intent) {
- final String packageName;
- // GlobalSearch puts the original source of the suggestion in the
- // 'component name' column. If set, we send the intent to that activity.
- // We trust GlobalSearch to always set this to the suggestion source.
- String intentComponent = intent.getStringExtra(SearchManager.COMPONENT_NAME_KEY);
- if (intentComponent != null) {
- ComponentName componentName = ComponentName.unflattenFromString(intentComponent);
- intent.setComponent(componentName);
- intent.removeExtra(SearchManager.COMPONENT_NAME_KEY);
- // Launch the intent as the suggestion source.
- // This prevents sources from using the search dialog to launch
- // intents that they don't have permission for themselves.
- packageName = componentName.getPackageName();
- } else {
- // If there is no component in the suggestion, it must be a built-in suggestion
- // from GlobalSearch (e.g. "Search the web for") or the intent
- // launched when pressing the search/go button in the search dialog.
- // Launch the intent with the permissions of GlobalSearch.
- packageName = mSearchable.getSearchActivity().getPackageName();
- }
-
- // Launch all global search suggestions as new tasks, since they don't relate
- // to the current task.
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- setBrowserApplicationId(intent);
-
- startActivityInPackage(intent, packageName);
- }
-
/**
* If the intent is to open an HTTP or HTTPS URL, we set
* {@link Browser#EXTRA_APPLICATION_ID} so that any existing browser window that
@@ -1372,106 +1092,6 @@
}
/**
- * Starts an activity as if it had been started by the given package.
- *
- * @param intent The description of the activity to start.
- * @param packageName
- * @throws ActivityNotFoundException If the intent could not be resolved to
- * and existing activity.
- * @throws SecurityException If the package does not have permission to start
- * start the activity.
- * @throws AndroidRuntimeException If some other error occurs.
- */
- private void startActivityInPackage(Intent intent, String packageName) {
- try {
- int uid = ActivityThread.getPackageManager().getPackageUid(packageName);
- if (uid < 0) {
- throw new AndroidRuntimeException("Package UID not found " + packageName);
- }
- String resolvedType = intent.resolveTypeIfNeeded(getContext().getContentResolver());
- IBinder resultTo = null;
- String resultWho = null;
- int requestCode = -1;
- boolean onlyIfNeeded = false;
- Log.i(LOG_TAG, "Starting (uid " + uid + ", " + packageName + ") " + intent.toURI());
- int result = ActivityManagerNative.getDefault().startActivityInPackage(
- uid, intent, resolvedType, resultTo, resultWho, requestCode, onlyIfNeeded);
- checkStartActivityResult(result, intent);
- } catch (RemoteException ex) {
- throw new AndroidRuntimeException(ex);
- }
- }
-
- // Stolen from Instrumentation.checkStartActivityResult()
- private static void checkStartActivityResult(int res, Intent intent) {
- if (res >= IActivityManager.START_SUCCESS) {
- return;
- }
- switch (res) {
- case IActivityManager.START_INTENT_NOT_RESOLVED:
- case IActivityManager.START_CLASS_NOT_FOUND:
- if (intent.getComponent() != null)
- throw new ActivityNotFoundException(
- "Unable to find explicit activity class "
- + intent.getComponent().toShortString()
- + "; have you declared this activity in your AndroidManifest.xml?");
- throw new ActivityNotFoundException(
- "No Activity found to handle " + intent);
- case IActivityManager.START_PERMISSION_DENIED:
- throw new SecurityException("Not allowed to start activity "
- + intent);
- case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
- throw new AndroidRuntimeException(
- "FORWARD_RESULT_FLAG used while also requesting a result");
- default:
- throw new AndroidRuntimeException("Unknown error code "
- + res + " when starting " + intent);
- }
- }
-
- /**
- * Handles the special intent actions declared in {@link SearchManager}.
- *
- * @return <code>true</code> if the intent was handled.
- */
- private boolean handleSpecialIntent(Intent intent) {
- String action = intent.getAction();
- if (SearchManager.INTENT_ACTION_CHANGE_SEARCH_SOURCE.equals(action)) {
- handleChangeSourceIntent(intent);
- return true;
- }
- return false;
- }
-
- /**
- * Handles {@link SearchManager#INTENT_ACTION_CHANGE_SEARCH_SOURCE}.
- */
- private void handleChangeSourceIntent(Intent intent) {
- Uri dataUri = intent.getData();
- if (dataUri == null) {
- Log.w(LOG_TAG, "SearchManager.INTENT_ACTION_CHANGE_SOURCE without intent data.");
- return;
- }
- ComponentName componentName = ComponentName.unflattenFromString(dataUri.toString());
- if (componentName == null) {
- Log.w(LOG_TAG, "Invalid ComponentName: " + dataUri);
- return;
- }
- if (DBG) Log.d(LOG_TAG, "Switching to " + componentName);
-
- pushPreviousComponent(mLaunchComponent);
- if (!show(componentName, mAppSearchData, false)) {
- Log.w(LOG_TAG, "Failed to switch to source " + componentName);
- popPreviousComponent();
- return;
- }
-
- String query = intent.getStringExtra(SearchManager.QUERY);
- setUserQuery(query);
- mSearchAutoComplete.showDropDown();
- }
-
- /**
* Sets the list item selection in the AutoCompleteTextView's ListView.
*/
public void setListSelection(int index) {
@@ -1479,61 +1099,6 @@
}
/**
- * Checks if there are any previous searchable components in the history stack.
- */
- private boolean hasPreviousComponent() {
- return mPreviousComponents != null && !mPreviousComponents.isEmpty();
- }
-
- /**
- * Saves the previous component that was searched, so that we can go
- * back to it.
- */
- private void pushPreviousComponent(ComponentName componentName) {
- if (mPreviousComponents == null) {
- mPreviousComponents = new ArrayList<ComponentName>();
- }
- mPreviousComponents.add(componentName);
- }
-
- /**
- * Pops the previous component off the stack and returns it.
- *
- * @return The component name, or <code>null</code> if there was
- * no previous component.
- */
- private ComponentName popPreviousComponent() {
- if (!hasPreviousComponent()) {
- return null;
- }
- return mPreviousComponents.remove(mPreviousComponents.size() - 1);
- }
-
- /**
- * Goes back to the previous component that was searched, if any.
- *
- * @return <code>true</code> if there was a previous component that we could go back to.
- */
- private boolean backToPreviousComponent() {
- ComponentName previous = popPreviousComponent();
- if (previous == null) {
- return false;
- }
-
- if (!show(previous, mAppSearchData, false)) {
- Log.w(LOG_TAG, "Failed to switch to source " + previous);
- return false;
- }
-
- // must touch text to trigger suggestions
- // TODO: should this be the text as it was when the user left
- // the source that we are now going back to?
- String query = mSearchAutoComplete.getText().toString();
- setUserQuery(query);
- return true;
- }
-
- /**
* When a particular suggestion has been selected, perform the various lookups required
* to use the suggestion. This includes checking the cursor for suggestion-specific data,
* and/or falling back to the XML for defaults; It also creates REST style Uri data when
@@ -1582,10 +1147,9 @@
String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY);
String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
- String mode = mGlobalSearchMode ? SearchManager.MODE_GLOBAL_SEARCH_SUGGESTION : null;
return createIntent(action, dataUri, extraData, query, componentName, actionKey,
- actionMsg, mode);
+ actionMsg);
} catch (RuntimeException e ) {
int rowNum;
try { // be really paranoid now
@@ -1616,16 +1180,13 @@
* @return The intent.
*/
private Intent createIntent(String action, Uri data, String extraData, String query,
- String componentName, int actionKey, String actionMsg, String mode) {
+ String componentName, int actionKey, String actionMsg) {
// Now build the Intent
Intent intent = new Intent(action);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// We need CLEAR_TOP to avoid reusing an old task that has other activities
// on top of the one we want. We don't want to do this in in-app search though,
// as it can be destructive to the activity stack.
- if (mGlobalSearchMode) {
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- }
if (data != null) {
intent.setData(data);
}
@@ -1636,9 +1197,6 @@
if (extraData != null) {
intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
}
- if (componentName != null) {
- intent.putExtra(SearchManager.COMPONENT_NAME_KEY, componentName);
- }
if (mAppSearchData != null) {
intent.putExtra(SearchManager.APP_DATA, mAppSearchData);
}
@@ -1646,16 +1204,10 @@
intent.putExtra(SearchManager.ACTION_KEY, actionKey);
intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
}
- if (mode != null) {
- intent.putExtra(SearchManager.SEARCH_MODE, mode);
- }
- // Only allow 3rd-party intents from GlobalSearch
- if (!mGlobalSearchMode) {
- intent.setComponent(mSearchable.getSearchActivity());
- }
+ intent.setComponent(mSearchable.getSearchActivity());
return intent;
}
-
+
/**
* For a given suggestion and a given cursor row, get the action message. If not provided
* by the specific row/column, also check for a single definition (for the action key).
@@ -1812,12 +1364,8 @@
imm.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0)) {
return;
}
- // Otherwise, go back to any previous source (e.g. back to QSB when
- // pivoted into a source.
- if (!backToPreviousComponent()) {
- // If no previous source, close search dialog
- cancel();
- }
+ // Close search dialog
+ cancel();
}
/**
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 5a9a675..52cdc74 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -350,7 +350,7 @@
* <p><b>Configuring your Content Provider to Receive Suggestion Queries.</b> The basic job of
* a search suggestions {@link android.content.ContentProvider Content Provider} is to provide
* "live" (while-you-type) conversion of the user's query text into a set of zero or more
- * suggestions. Each application is free to define the conversion, and as described above there are
+ * suggestions. Each application is free to define the conversion, and as described above there are
* many possible solutions. This section simply defines how to communicate with the suggestion
* provider.
*
@@ -360,7 +360,8 @@
*
* <p>Every query includes a Uri, and the Search Manager will format the Uri as shown:
* <p><pre class="prettyprint">
- * content:// your.suggest.authority / your.suggest.path / SearchManager.SUGGEST_URI_PATH_QUERY</pre>
+ * content:// your.suggest.authority / your.suggest.path / SearchManager.SUGGEST_URI_PATH_QUERY
+ * </pre>
*
* <p>Your Content Provider can receive the query text in one of two ways.
* <ul>
@@ -379,7 +380,7 @@
* <p><b>Providing access to Content Providers that require permissions.</b> If your content
* provider declares an android:readPermission in your application's manifest, you must provide
* access to the search infrastructure to the search suggestion path by including a path-permission
- * that grants android:readPermission access to "android.permission.GLOBAL_SEARCH". Granting access
+ * that grants android:readPermission access to "android.permission.GLOBAL_SEARCH". Granting access
* explicitly to the search infrastructure ensures it will be able to access the search suggestions
* without needing to know ahead of time any other details of the permissions protecting your
* provider. Content providers that require no permissions are already available to the search
@@ -546,7 +547,8 @@
* taken directly to a specific result.
* <ul>
* <li><b>Action:</b> {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}</li>
- * <li><b>Data:</b> a complete Uri, supplied by the cursor, that identifies the desired data.</li>
+ * <li><b>Data:</b> a complete Uri, supplied by the cursor, that identifies the desired data.
+ * </li>
* <li><b>Query:</b> query text supplied with the suggestion (probably ignored)</li>
* </ul>
* </li>
@@ -570,7 +572,7 @@
*
* <p><b>Suggestion Rewriting.</b> If the user navigates through the suggestions list, the UI
* may temporarily rewrite the user's query with a query that matches the currently selected
- * suggestion. This enables the user to see what query is being suggested, and also allows the user
+ * suggestion. This enables the user to see what query is being suggested, and also allows the user
* to click or touch in the entry EditText element and make further edits to the query before
* dispatching it. In order to perform this correctly, the Search UI needs to know exactly what
* text to rewrite the query with.
@@ -1289,15 +1291,6 @@
public final static String SEARCH_MODE = "search_mode";
/**
- * Value for the {@link #SEARCH_MODE} key.
- * This is used if the intent was launched by clicking a suggestion in global search
- * mode (Quick Search Box).
- *
- * @hide
- */
- public static final String MODE_GLOBAL_SEARCH_SUGGESTION = "global_search_suggestion";
-
- /**
* Intent extra data key: Use this key with Intent.ACTION_SEARCH and
* {@link android.content.Intent#getIntExtra content.Intent.getIntExtra()}
* to obtain the keycode that the user used to trigger this query. It will be zero if the
@@ -1308,14 +1301,6 @@
public final static String ACTION_KEY = "action_key";
/**
- * Intent component name key: This key will be used for the extra populated by the
- * {@link #SUGGEST_COLUMN_INTENT_COMPONENT_NAME} column.
- *
- * {@hide}
- */
- public final static String COMPONENT_NAME_KEY = "intent_component_name_key";
-
- /**
* Intent extra data key: This key will be used for the extra populated by the
* {@link #SUGGEST_COLUMN_INTENT_EXTRA_DATA} column.
*/
@@ -1329,58 +1314,6 @@
public final static String EXTRA_SELECT_QUERY = "select_query";
/**
- * Defines the constants used in the communication between {@link android.app.SearchDialog} and
- * the global search provider via {@link Cursor#respond(android.os.Bundle)}.
- *
- * @hide
- */
- public static class DialogCursorProtocol {
-
- /**
- * The sent bundle will contain this integer key, with a value set to one of the events
- * below.
- */
- public final static String METHOD = "DialogCursorProtocol.method";
-
- /**
- * After data has been refreshed.
- */
- public final static int POST_REFRESH = 0;
- public final static String POST_REFRESH_RECEIVE_ISPENDING
- = "DialogCursorProtocol.POST_REFRESH.isPending";
- public final static String POST_REFRESH_RECEIVE_DISPLAY_NOTIFY
- = "DialogCursorProtocol.POST_REFRESH.displayNotify";
-
- /**
- * When a position has been clicked.
- */
- public final static int CLICK = 2;
- public final static String CLICK_SEND_POSITION
- = "DialogCursorProtocol.CLICK.sendPosition";
- public final static String CLICK_SEND_MAX_DISPLAY_POS
- = "DialogCursorProtocol.CLICK.sendDisplayPosition";
- public final static String CLICK_SEND_ACTION_KEY
- = "DialogCursorProtocol.CLICK.sendActionKey";
- public final static String CLICK_SEND_ACTION_MSG
- = "DialogCursorProtocol.CLICK.sendActionMsg";
- public final static String CLICK_RECEIVE_SELECTED_POS
- = "DialogCursorProtocol.CLICK.receiveSelectedPosition";
-
- /**
- * When the threshold received in {@link #POST_REFRESH_RECEIVE_DISPLAY_NOTIFY} is displayed.
- */
- public final static int THRESH_HIT = 3;
-
- /**
- * When a search is started without using a suggestion.
- */
- public final static int SEARCH = 4;
- public final static String SEARCH_SEND_MAX_DISPLAY_POS
- = "DialogCursorProtocol.SEARCH.sendDisplayPosition";
- public final static String SEARCH_SEND_QUERY = "DialogCursorProtocol.SEARCH.query";
- }
-
- /**
* Intent extra data key: Use this key with Intent.ACTION_SEARCH and
* {@link android.content.Intent#getStringExtra content.Intent.getStringExtra()}
* to obtain the action message that was defined for a particular search action key and/or
@@ -1421,40 +1354,6 @@
public final static String SHORTCUT_MIME_TYPE =
"vnd.android.cursor.item/vnd.android.search.suggest";
-
- /**
- * The authority of the provider to report clicks to when a click is detected after pivoting
- * into a specific app's search from global search.
- *
- * In addition to the columns below, the suggestion columns are used to pass along the full
- * suggestion so it can be shortcutted.
- *
- * @hide
- */
- public final static String SEARCH_CLICK_REPORT_AUTHORITY =
- "com.android.globalsearch.stats";
-
- /**
- * The path the write goes to.
- *
- * @hide
- */
- public final static String SEARCH_CLICK_REPORT_URI_PATH = "click";
-
- /**
- * The column storing the query for the click.
- *
- * @hide
- */
- public final static String SEARCH_CLICK_REPORT_COLUMN_QUERY = "query";
-
- /**
- * The column storing the component name of the application that was pivoted into.
- *
- * @hide
- */
- public final static String SEARCH_CLICK_REPORT_COLUMN_COMPONENT = "component";
-
/**
* Column name for suggestions cursor. <i>Unused - can be null or column can be omitted.</i>
*/
@@ -1531,10 +1430,7 @@
*/
public final static String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data";
/**
- * Column name for suggestions cursor. <i>Optional.</i> This column allows suggestions
- * to provide additional arbitrary data which will be included as an extra under the key
- * {@link #COMPONENT_NAME_KEY}. For use by the global search system only - if other providers
- * attempt to use this column, the value will be overwritten by global search.
+ * TODO: Remove
*
* @hide
*/
@@ -1594,27 +1490,6 @@
public final static String SUGGEST_PARAMETER_LIMIT = "limit";
/**
- * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION},
- * the search dialog will switch to a different suggestion source when the
- * suggestion is clicked.
- *
- * {@link #SUGGEST_COLUMN_INTENT_DATA} must contain
- * the flattened {@link ComponentName} of the activity which is to be searched.
- *
- * TODO: Should {@link #SUGGEST_COLUMN_INTENT_DATA} instead contain a URI in the format
- * used by {@link android.provider.Applications}?
- *
- * TODO: This intent should be protected by the same permission that we use
- * for replacing the global search provider.
- *
- * The query text field will be set to the value of {@link #SUGGEST_COLUMN_QUERY}.
- *
- * @hide Pending API council approval.
- */
- public final static String INTENT_ACTION_CHANGE_SEARCH_SOURCE
- = "android.search.action.CHANGE_SEARCH_SOURCE";
-
- /**
* Intent action for starting the global search activity.
* The global search provider should handle this intent.
*
@@ -1663,7 +1538,7 @@
* @hide
*/
public final static String INTENT_ACTION_NONE = "android.search.action.ZILCH";
-
+
/**
* Reference to the shared system search service.
*/
@@ -1672,13 +1547,6 @@
private final Context mContext;
/**
- * compact representation of the activity associated with this search manager so
- * we can say who we are when starting search. the search managerservice, in turn,
- * uses this to properly handle the back stack.
- */
- private int mIdent;
-
- /**
* The package associated with this seach manager.
*/
private String mAssociatedPackage;
@@ -1696,21 +1564,6 @@
mService = ISearchManager.Stub.asInterface(
ServiceManager.getService(Context.SEARCH_SERVICE));
}
-
- /*package*/ boolean hasIdent() {
- return mIdent != 0;
- }
-
- /*package*/ void setIdent(int ident, ComponentName component) {
- if (mIdent != 0) {
- throw new IllegalStateException("mIdent already set");
- }
- if (component == null) {
- throw new IllegalArgumentException("component must be non-null");
- }
- mIdent = ident;
- mAssociatedPackage = component.getPackageName();
- }
/**
* Launch search UI.
@@ -1757,15 +1610,14 @@
ComponentName launchActivity,
Bundle appSearchData,
boolean globalSearch) {
- ensureSearchDialog();
-
if (globalSearch) {
startGlobalSearch(initialQuery, selectInitialQuery, appSearchData);
return;
}
- mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData,
- globalSearch);
+ ensureSearchDialog();
+
+ mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData);
}
private void ensureSearchDialog() {
@@ -1994,17 +1846,6 @@
return null;
}
}
-
- /**
- * Checks whether the given searchable is the default searchable.
- *
- * @hide because SearchableInfo is not part of the API.
- */
- public boolean isDefaultSearchable(SearchableInfo searchable) {
- SearchableInfo defaultSearchable = getSearchableInfo(null, true);
- return defaultSearchable != null
- && defaultSearchable.getSearchActivity().equals(searchable.getSearchActivity());
- }
/**
* Gets a cursor with search suggestions.
@@ -2091,56 +1932,4 @@
}
}
- /**
- * Returns a list of the searchable activities that handle web searches.
- *
- * @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.
- */
- public List<SearchableInfo> getSearchablesForWebSearch() {
- try {
- return mService.getSearchablesForWebSearch();
- } catch (RemoteException e) {
- Log.e(TAG, "getSearchablesForWebSearch() failed: " + e);
- return null;
- }
- }
-
- /**
- * Returns the default searchable activity for web searches.
- *
- * @return searchable information for the activity handling web searches by default.
- *
- * @hide because SearchableInfo is not part of the API.
- */
- public SearchableInfo getDefaultSearchableForWebSearch() {
- try {
- return mService.getDefaultSearchableForWebSearch();
- } catch (RemoteException e) {
- Log.e(TAG, "getDefaultSearchableForWebSearch() failed: " + e);
- return null;
- }
- }
-
- /**
- * Sets the default searchable activity for web searches.
- *
- * @param component Name of the component to set as default activity for web searches.
- *
- * @hide
- */
- public void setDefaultWebSearch(ComponentName component) {
- try {
- mService.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() + ")");
- }
}
diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java
index 173c3e1..57795d1 100644
--- a/core/java/android/app/SuggestionsAdapter.java
+++ b/core/java/android/app/SuggestionsAdapter.java
@@ -16,7 +16,6 @@
package android.app;
-import android.app.SearchManager.DialogCursorProtocol;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -30,12 +29,10 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.net.Uri;
-import android.os.Bundle;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
-import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
@@ -65,7 +62,6 @@
private Context mProviderContext;
private WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache;
private SparseArray<Drawable.ConstantState> mBackgroundsCache;
- private boolean mGlobalSearchMode;
private boolean mClosed = false;
// Cached column indexes, updated when the cursor changes.
@@ -76,29 +72,8 @@
private int mIconName2Col;
private int mBackgroundColorCol;
- // The extra used to tell a cursor to close itself. This is a hack, see the description by
- // its use later in this file.
- private static final String EXTRA_CURSOR_RESPOND_CLOSE_CURSOR = "cursor_respond_close_cursor";
-
- // The bundle which contains {EXTRA_CURSOR_RESPOND_CLOSE_CURSOR=true}, just cached once
- // so we don't bother recreating it a bunch.
- private final Bundle mCursorRespondCloseCursorBundle;
-
- // This value is stored in SuggestionsAdapter by the SearchDialog to indicate whether
- // a particular list item should be selected upon the next call to notifyDataSetChanged.
- // This is used to indicate the index of the "More results..." list item so that when
- // the data set changes after a click of "More results...", we can correctly tell the
- // ListView to scroll to the right line item. It gets reset to NONE every time it
- // is consumed.
- private int mListItemToSelect = NONE;
static final int NONE = -1;
- // holds the maximum position that has been displayed to the user
- int mMaxDisplayed = NONE;
-
- // holds the position that, when displayed, should result in notifying the cursor
- int mDisplayNotifyPos = NONE;
-
private final Runnable mStartSpinnerRunnable;
private final Runnable mStopSpinnerRunnable;
@@ -110,8 +85,7 @@
public SuggestionsAdapter(Context context, SearchDialog searchDialog,
SearchableInfo searchable,
- WeakHashMap<String, Drawable.ConstantState> outsideDrawablesCache,
- boolean globalSearchMode) {
+ WeakHashMap<String, Drawable.ConstantState> outsideDrawablesCache) {
super(context,
com.android.internal.R.layout.search_dropdown_item_icons_2line,
null, // no initial cursor
@@ -126,7 +100,6 @@
mOutsideDrawablesCache = outsideDrawablesCache;
mBackgroundsCache = new SparseArray<Drawable.ConstantState>();
- mGlobalSearchMode = globalSearchMode;
mStartSpinnerRunnable = new Runnable() {
public void run() {
@@ -140,10 +113,6 @@
}
};
- // Create this once because we'll reuse it a bunch.
- mCursorRespondCloseCursorBundle = new Bundle();
- mCursorRespondCloseCursorBundle.putBoolean(EXTRA_CURSOR_RESPOND_CLOSE_CURSOR, true);
-
// delay 500ms when deleting
getFilter().setDelayer(new Filter.Delayer() {
@@ -177,27 +146,22 @@
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
if (DBG) Log.d(LOG_TAG, "runQueryOnBackgroundThread(" + constraint + ")");
String query = (constraint == null) ? "" : constraint.toString();
- if (!mGlobalSearchMode) {
- /**
- * for in app search we show the progress spinner until the cursor is returned with
- * the results. for global search we manage the progress bar using
- * {@link DialogCursorProtocol#POST_REFRESH_RECEIVE_ISPENDING}.
- */
- mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable);
- }
+ /**
+ * for in app search we show the progress spinner until the cursor is returned with
+ * the results.
+ */
+ mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable);
try {
final Cursor cursor = mSearchManager.getSuggestions(mSearchable, query, QUERY_LIMIT);
// trigger fill window so the spinner stays up until the results are copied over and
// closer to being ready
- if (!mGlobalSearchMode && cursor != null) cursor.getCount();
+ if (cursor != null) cursor.getCount();
return cursor;
} catch (RuntimeException e) {
Log.w(LOG_TAG, "Search suggestions query threw an exception.", e);
return null;
} finally {
- if (!mGlobalSearchMode) {
- mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable);
- }
+ mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable);
}
}
@@ -221,21 +185,8 @@
}
try {
- Cursor oldCursor = getCursor();
super.changeCursor(c);
-
- // We send a special respond to the cursor to tell it to close itself directly because
- // it may not happen correctly for some cursors currently. This was originally
- // included as a fix to http://b/2036290, in which the search dialog was holding
- // on to references to the web search provider unnecessarily. This is being caused by
- // the fact that the cursor is not being correctly closed in
- // BulkCursorToCursorAdapter#close, which remains unfixed (see http://b/2015069).
- //
- // TODO: Remove this hack once http://b/2015069 is fixed.
- if (oldCursor != null && oldCursor != c) {
- oldCursor.respond(mCursorRespondCloseCursorBundle);
- }
-
+
if (c != null) {
mFormatCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_FORMAT);
mText1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
@@ -249,79 +200,6 @@
}
}
- @Override
- public void notifyDataSetChanged() {
- if (DBG) Log.d(LOG_TAG, "notifyDataSetChanged");
- super.notifyDataSetChanged();
-
- callCursorPostRefresh(mCursor);
-
- // look out for the pending item we are supposed to scroll to
- if (mListItemToSelect != NONE) {
- mSearchDialog.setListSelection(mListItemToSelect);
- mListItemToSelect = NONE;
- }
- }
-
- /**
- * Handle sending and receiving information associated with
- * {@link DialogCursorProtocol#POST_REFRESH}.
- *
- * @param cursor The cursor to call.
- */
- private void callCursorPostRefresh(Cursor cursor) {
- if (!mGlobalSearchMode) return;
- final Bundle request = new Bundle();
- request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.POST_REFRESH);
- final Bundle response = cursor.respond(request);
-
- mSearchDialog.setWorking(
- response.getBoolean(DialogCursorProtocol.POST_REFRESH_RECEIVE_ISPENDING, false));
-
- mDisplayNotifyPos =
- response.getInt(DialogCursorProtocol.POST_REFRESH_RECEIVE_DISPLAY_NOTIFY, -1);
- }
-
- /**
- * Tell the cursor which position was clicked, handling sending and receiving information
- * associated with {@link DialogCursorProtocol#CLICK}.
- *
- * @param cursor The cursor
- * @param position The position that was clicked.
- */
- void callCursorOnClick(Cursor cursor, int position, int actionKey, String actionMsg) {
- if (!mGlobalSearchMode) return;
- final Bundle request = new Bundle(5);
- request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.CLICK);
- request.putInt(DialogCursorProtocol.CLICK_SEND_POSITION, position);
- request.putInt(DialogCursorProtocol.CLICK_SEND_MAX_DISPLAY_POS, mMaxDisplayed);
- if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
- request.putInt(DialogCursorProtocol.CLICK_SEND_ACTION_KEY, actionKey);
- request.putString(DialogCursorProtocol.CLICK_SEND_ACTION_MSG, actionMsg);
- }
- final Bundle response = cursor.respond(request);
- mMaxDisplayed = -1;
- mListItemToSelect = response.getInt(
- DialogCursorProtocol.CLICK_RECEIVE_SELECTED_POS, SuggestionsAdapter.NONE);
- }
-
- /**
- * Tell the cursor that a search was started without using a suggestion.
- *
- * @param query The search query.
- */
- void reportSearch(String query) {
- if (!mGlobalSearchMode) return;
- Cursor cursor = getCursor();
- if (cursor == null) return;
- final Bundle request = new Bundle(3);
- request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.SEARCH);
- request.putString(DialogCursorProtocol.SEARCH_SEND_QUERY, query);
- request.putInt(DialogCursorProtocol.SEARCH_SEND_MAX_DISPLAY_POS, mMaxDisplayed);
- // the response is always empty
- cursor.respond(request);
- }
-
/**
* Tags the view with cached child view look-ups.
*/
@@ -353,20 +231,6 @@
@Override
public void bindView(View view, Context context, Cursor cursor) {
ChildViewCache views = (ChildViewCache) view.getTag();
- final int pos = cursor.getPosition();
-
- // update the maximum position displayed since last refresh
- if (pos > mMaxDisplayed) {
- mMaxDisplayed = pos;
- }
-
- // if the cursor wishes to be notified about this position, send it
- if (mGlobalSearchMode && mDisplayNotifyPos != NONE && pos == mDisplayNotifyPos) {
- final Bundle request = new Bundle();
- request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.THRESH_HIT);
- mCursor.respond(request);
- mDisplayNotifyPos = NONE; // only notify the first time
- }
int backgroundColor = 0;
if (mBackgroundColorCol != -1) {
diff --git a/core/java/android/text/AndroidCharacter.java b/core/java/android/text/AndroidCharacter.java
index af93b5d..05887c5 100644
--- a/core/java/android/text/AndroidCharacter.java
+++ b/core/java/android/text/AndroidCharacter.java
@@ -73,6 +73,11 @@
* Replace the specified slice of <code>text</code> with the chars'
* right-to-left mirrors (if any), returning true if any
* replacements were made.
+ *
+ * @param text array of characters to apply mirror operation
+ * @param start first character in array to mirror
+ * @param count maximum number of characters to mirror
+ * @return true if replacements were made
*/
public native static boolean mirror(char[] text, int start, int count);
diff --git a/core/jni/android_text_AndroidCharacter.cpp b/core/jni/android_text_AndroidCharacter.cpp
index 1353478..5d8d419 100644
--- a/core/jni/android_text_AndroidCharacter.cpp
+++ b/core/jni/android_text_AndroidCharacter.cpp
@@ -165,7 +165,8 @@
goto MIRROR_END;
}
- if (start > start + count || env->GetArrayLength(charArray) < count) {
+ if (start < 0 || start > start + count
+ || env->GetArrayLength(charArray) < start + count) {
jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
goto MIRROR_END;
}
diff --git a/core/tests/coretests/src/android/app/SearchManagerTest.java b/core/tests/coretests/src/android/app/SearchManagerTest.java
index fc7e787..08b7f60 100644
--- a/core/tests/coretests/src/android/app/SearchManagerTest.java
+++ b/core/tests/coretests/src/android/app/SearchManagerTest.java
@@ -120,23 +120,6 @@
assertSame(searchManager1, searchManager2 );
}
- @MediumTest
- public void testSearchables() {
- SearchManager searchManager = (SearchManager)
- mContext.getSystemService(Context.SEARCH_SERVICE);
- 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 startSearch() can be called multiple times without stopSearch()
* in between.
diff --git a/core/tests/hosttests/Android.mk b/core/tests/hosttests/Android.mk
new file mode 100644
index 0000000..0001201
--- /dev/null
+++ b/core/tests/hosttests/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+#LOCAL_TEST_TYPE := hostSideOnly
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := FrameworkCoreHostTests
+
+LOCAL_JAVA_LIBRARIES := hosttestlib ddmlib junit
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+# Build the test APKs using their own makefiles
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/core/tests/hosttests/README b/core/tests/hosttests/README
new file mode 100644
index 0000000..d3bdb83
--- /dev/null
+++ b/core/tests/hosttests/README
@@ -0,0 +1,6 @@
+This dir contains tests which run on a host machine, and test aspects of
+package install etc via adb.
+
+To run, do:
+runtest framework-core-host
+
diff --git a/core/tests/hosttests/src/android/content/pm/PackageManagerHostTests.java b/core/tests/hosttests/src/android/content/pm/PackageManagerHostTests.java
new file mode 100644
index 0000000..9c9d777
--- /dev/null
+++ b/core/tests/hosttests/src/android/content/pm/PackageManagerHostTests.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import com.android.ddmlib.IShellOutputReceiver;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.MultiLineReceiver;
+import com.android.ddmlib.SyncService;
+import com.android.ddmlib.SyncService.ISyncProgressMonitor;
+import com.android.ddmlib.SyncService.SyncResult;
+import com.android.hosttest.DeviceTestCase;
+import com.android.hosttest.DeviceTestSuite;
+
+import java.io.File;
+import java.io.IOException;
+
+import junit.framework.Test;
+
+/**
+ * Set of tests that verify host side install cases
+ */
+public class PackageManagerHostTests extends DeviceTestCase {
+
+ // testPushAppPrivate constants
+ // these constants must match values defined in test-apps/SimpleTestApp
+ private static final String SIMPLE_APK = "SimpleTestApp.apk";
+ private static final String SIMPLE_PKG = "com.android.framework.simpletestapp";
+ // TODO: get this value from Android Environment instead of hardcoding
+ private static final String APP_PRIVATE_PATH = "/data/app-private/";
+
+ private static final String LOG_TAG = "PackageManagerHostTests";
+
+ private static final int MAX_WAIT_FOR_DEVICE_TIME = 120 * 1000;
+ private static final int WAIT_FOR_DEVICE_POLL_TIME = 10 * 1000;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ // ensure apk path has been set before test is run
+ assertNotNull(getTestAppPath());
+ }
+
+ /**
+ * Regression test to verify that pushing an apk to the private app directory doesn't install
+ * the app, and otherwise cause the system to blow up.
+ * <p/>
+ * Assumes adb is running as root in device under test.
+ */
+ public void testPushAppPrivate() throws IOException, InterruptedException {
+ Log.i(LOG_TAG, "testing pushing an apk to /data/app-private");
+ final String apkAppPrivatePath = APP_PRIVATE_PATH + SIMPLE_APK;
+
+ // cleanup test app just in case it was accidently installed
+ getDevice().uninstallPackage(SIMPLE_PKG);
+ executeShellCommand("stop");
+ pushFile(getTestAppFilePath(SIMPLE_APK), apkAppPrivatePath);
+ // sanity check to make sure file is there
+ assertTrue(doesRemoteFileExist(apkAppPrivatePath));
+ executeShellCommand("start");
+
+ waitForDevice();
+
+ // grep for package to make sure its not installed
+ assertFalse(doesPackageExist(SIMPLE_PKG));
+ // ensure it has been deleted from app-private
+ assertFalse(doesRemoteFileExist(apkAppPrivatePath));
+ }
+
+ /**
+ * Helper method to push a file to device
+ * @param apkAppPrivatePath
+ * @throws IOException
+ */
+ private void pushFile(final String localFilePath, final String destFilePath)
+ throws IOException {
+ SyncResult result = getDevice().getSyncService().pushFile(
+ localFilePath, destFilePath,
+ new NullSyncProgressMonitor());
+ assertEquals(SyncService.RESULT_OK, result.getCode());
+ }
+
+ /**
+ * Helper method to determine if file on device exists.
+ *
+ * @param destPath the absolute path of file on device to check
+ * @return <code>true</code> if file exists, <code>false</code> otherwise.
+ * @throws IOException if adb shell command failed
+ */
+ private boolean doesRemoteFileExist(String destPath) throws IOException {
+ String lsGrep = executeShellCommand(String.format("ls %s",
+ destPath));
+ return !lsGrep.contains("No such file or directory");
+ }
+
+ /**
+ * Helper method to determine if package on device exists.
+ *
+ * @param packageName the Android manifest package to check.
+ * @return <code>true</code> if package exists, <code>false</code> otherwise
+ * @throws IOException if adb shell command failed
+ */
+ private boolean doesPackageExist(String packageName) throws IOException {
+ String pkgGrep = executeShellCommand(String.format("pm path %s",
+ packageName));
+ return pkgGrep.contains("package:");
+ }
+
+ /**
+ * Waits for device's package manager to respond.
+ *
+ * @throws InterruptedException
+ * @throws IOException
+ */
+ private void waitForDevice() throws InterruptedException, IOException {
+ Log.i(LOG_TAG, "waiting for device");
+ int currentWaitTime = 0;
+ // poll the package manager until it returns something for android
+ while (!doesPackageExist("android")) {
+ Thread.sleep(WAIT_FOR_DEVICE_POLL_TIME);
+ currentWaitTime += WAIT_FOR_DEVICE_POLL_TIME;
+ if (currentWaitTime > MAX_WAIT_FOR_DEVICE_TIME) {
+ Log.e(LOG_TAG, "time out waiting for device");
+ throw new InterruptedException();
+ }
+ }
+ }
+
+ /**
+ * Helper method which executes a adb shell command and returns output as a {@link String}
+ * @return
+ * @throws IOException
+ */
+ private String executeShellCommand(String command) throws IOException {
+ Log.d(LOG_TAG, String.format("adb shell %s", command));
+ CollectingOutputReceiver receiver = new CollectingOutputReceiver();
+ getDevice().executeShellCommand(command, receiver);
+ String output = receiver.getOutput();
+ Log.d(LOG_TAG, String.format("Result: %s", output));
+ return output;
+ }
+
+ /**
+ * Get the absolute file system location of test app with given filename
+ * @param fileName the file name of the test app apk
+ * @return {@link String} of absolute file path
+ */
+ private String getTestAppFilePath(String fileName) {
+ return String.format("%s%s%s", getTestAppPath(), File.separator, fileName);
+ }
+
+ public static Test suite() {
+ return new DeviceTestSuite(PackageManagerHostTests.class);
+ }
+
+ /**
+ * A {@link IShellOutputReceiver} which collects the whole shell output into one {@link String}
+ */
+ private static class CollectingOutputReceiver extends MultiLineReceiver {
+
+ private StringBuffer mOutputBuffer = new StringBuffer();
+
+ public String getOutput() {
+ return mOutputBuffer.toString();
+ }
+
+ @Override
+ public void processNewLines(String[] lines) {
+ for (String line: lines) {
+ mOutputBuffer.append(line);
+ mOutputBuffer.append("\n");
+ }
+ }
+
+ public boolean isCancelled() {
+ return false;
+ }
+ }
+
+ private static class NullSyncProgressMonitor implements ISyncProgressMonitor {
+ public void advance(int work) {
+ // ignore
+ }
+
+ public boolean isCanceled() {
+ // ignore
+ return false;
+ }
+
+ public void start(int totalWork) {
+ // ignore
+
+ }
+
+ public void startSubTask(String name) {
+ // ignore
+ }
+
+ public void stop() {
+ // ignore
+ }
+ }
+}
diff --git a/core/tests/hosttests/test-apps/Android.mk b/core/tests/hosttests/test-apps/Android.mk
new file mode 100644
index 0000000..e25764f
--- /dev/null
+++ b/core/tests/hosttests/test-apps/Android.mk
@@ -0,0 +1,21 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Build the test APKs using their own makefiles
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/core/tests/hosttests/test-apps/SimpleTestApp/Android.mk b/core/tests/hosttests/test-apps/SimpleTestApp/Android.mk
new file mode 100644
index 0000000..82543aa
--- /dev/null
+++ b/core/tests/hosttests/test-apps/SimpleTestApp/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := SimpleTestApp
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/SimpleTestApp/AndroidManifest.xml b/core/tests/hosttests/test-apps/SimpleTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..ae36fe7
--- /dev/null
+++ b/core/tests/hosttests/test-apps/SimpleTestApp/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.simpletestapp">
+
+ <!--
+ A simple empty app used to test various install scenarios/
+ -->
+
+</manifest>
diff --git a/core/tests/hosttests/test-apps/SimpleTestApp/src/com/android/framework/simpletestapp/SimpleAppActivity.java b/core/tests/hosttests/test-apps/SimpleTestApp/src/com/android/framework/simpletestapp/SimpleAppActivity.java
new file mode 100644
index 0000000..b7bbf94
--- /dev/null
+++ b/core/tests/hosttests/test-apps/SimpleTestApp/src/com/android/framework/simpletestapp/SimpleAppActivity.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.framework.simpletestapp;
+
+import android.app.Activity;
+
+/**
+ * Empty activity, not needed for this test
+ */
+public class SimpleAppActivity extends Activity {
+
+}
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 586f63f..6797f78 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1654,7 +1654,7 @@
if (DBG) log("processPlusCodeWithinNanp,networkDialStr=" + networkDialStr);
// If there is a plus sign at the beginning of the dial string,
// Convert the plus sign to the default IDP since it's an international number
- if (networkDialStr != null &
+ if (networkDialStr != null &&
networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
networkDialStr.length() > 1) {
String newStr = networkDialStr.substring(1);