diff options
25 files changed, 829 insertions, 250 deletions
diff --git a/Android.mk b/Android.mk index bffd04cdb9f5..6e292a8f6d1b 100644 --- a/Android.mk +++ b/Android.mk @@ -76,6 +76,7 @@ LOCAL_SRC_FILES += \ core/java/android/app/IIntentSender.aidl \ core/java/android/app/INotificationManager.aidl \ core/java/android/app/ISearchManager.aidl \ + core/java/android/app/ISearchManagerCallback.aidl \ core/java/android/app/IServiceConnection.aidl \ core/java/android/app/IStatusBar.aidl \ core/java/android/app/IThumbnailReceiver.aidl \ diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index f9b3d050248c..7fb3449fcbf9 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -628,6 +628,8 @@ public class Activity extends ContextThemeWrapper boolean mStartedActivity; /*package*/ int mConfigChangeFlags; /*package*/ Configuration mCurrentConfig; + private SearchManager mSearchManager; + private Bundle mSearchDialogState = null; private Window mWindow; @@ -788,6 +790,9 @@ public class Activity extends ContextThemeWrapper protected void onCreate(Bundle savedInstanceState) { mVisibleFromClient = mWindow.getWindowStyle().getBoolean( com.android.internal.R.styleable.Window_windowNoDisplay, true); + // uses super.getSystemService() since this.getSystemService() looks at the + // mSearchManager field. + mSearchManager = (SearchManager) super.getSystemService(Context.SEARCH_SERVICE); mCalled = true; } @@ -805,9 +810,10 @@ public class Activity extends ContextThemeWrapper // Also restore the state of a search dialog (if any) // TODO more generic than just this manager - SearchManager searchManager = - (SearchManager) getSystemService(Context.SEARCH_SERVICE); - searchManager.restoreSearchDialog(savedInstanceState, SAVED_SEARCH_DIALOG_KEY); + Bundle searchState = savedInstanceState.getBundle(SAVED_SEARCH_DIALOG_KEY); + if (searchState != null) { + mSearchManager.restoreSearchDialog(searchState); + } } /** @@ -1013,9 +1019,11 @@ public class Activity extends ContextThemeWrapper // Also save the state of a search dialog (if any) // TODO more generic than just this manager - SearchManager searchManager = - (SearchManager) getSystemService(Context.SEARCH_SERVICE); - searchManager.saveSearchDialog(outState, SAVED_SEARCH_DIALOG_KEY); + // onPause() should always be called before this method, so mSearchManagerState + // should be up to date. + if (mSearchDialogState != null) { + outState.putBundle(SAVED_SEARCH_DIALOG_KEY, mSearchDialogState); + } } /** @@ -1286,12 +1294,6 @@ public class Activity extends ContextThemeWrapper } } } - - // also dismiss search dialog if showing - // TODO more generic than just this manager - SearchManager searchManager = - (SearchManager) getSystemService(Context.SEARCH_SERVICE); - searchManager.stopSearch(); // close any cursors we are managing. int numCursors = mManagedCursors.size(); @@ -1301,6 +1303,10 @@ public class Activity extends ContextThemeWrapper c.mCursor.close(); } } + + // Clear any search state saved in performPause(). If the state may be needed in the + // future, it will have been saved by performSaveInstanceState() + mSearchDialogState = null; } /** @@ -1324,9 +1330,7 @@ public class Activity extends ContextThemeWrapper // also update search dialog if showing // TODO more generic than just this manager - SearchManager searchManager = - (SearchManager) getSystemService(Context.SEARCH_SERVICE); - searchManager.onConfigurationChanged(newConfig); + mSearchManager.onConfigurationChanged(newConfig); if (mWindow != null) { // Pass the configuration changed event to the window @@ -2543,10 +2547,7 @@ public class Activity extends ContextThemeWrapper */ public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, boolean globalSearch) { - // activate the search manager and start it up! - SearchManager searchManager = (SearchManager) - getSystemService(Context.SEARCH_SERVICE); - searchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(), + mSearchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(), appSearchData, globalSearch); } @@ -3265,6 +3266,8 @@ public class Activity extends ContextThemeWrapper if (WINDOW_SERVICE.equals(name)) { return mWindowManager; + } else if (SEARCH_SERVICE.equals(name)) { + return mSearchManager; } return super.getSystemService(name); } @@ -3563,10 +3566,21 @@ public class Activity extends ContextThemeWrapper "Activity " + mComponent.toShortString() + " did not call through to super.onPostResume()"); } + + // restore search dialog, if any + if (mSearchDialogState != null) { + mSearchManager.restoreSearchDialog(mSearchDialogState); + } + mSearchDialogState = null; } final void performPause() { onPause(); + + // save search dialog state if the search dialog is open, + // and then dismiss the search dialog + mSearchDialogState = mSearchManager.saveSearchDialog(); + mSearchManager.stopSearch(); } final void performUserLeaving() { diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl index 374423e22799..e8bd60a4d86f 100644 --- a/core/java/android/app/ISearchManager.aidl +++ b/core/java/android/app/ISearchManager.aidl @@ -16,7 +16,10 @@ package android.app; +import android.app.ISearchManagerCallback; import android.content.ComponentName; +import android.content.res.Configuration; +import android.os.Bundle; import android.server.search.SearchableInfo; /** @hide */ @@ -26,4 +29,15 @@ interface ISearchManager { List<SearchableInfo> getSearchablesForWebSearch(); SearchableInfo getDefaultSearchableForWebSearch(); void setDefaultWebSearch(in ComponentName component); + void startSearch(in String initialQuery, + boolean selectInitialQuery, + in ComponentName launchActivity, + in Bundle appSearchData, + boolean globalSearch, + ISearchManagerCallback searchManagerCallback); + void stopSearch(); + boolean isVisible(); + Bundle onSaveInstanceState(); + void onRestoreInstanceState(in Bundle savedInstanceState); + void onConfigurationChanged(in Configuration newConfig); } diff --git a/core/java/android/app/ISearchManagerCallback.aidl b/core/java/android/app/ISearchManagerCallback.aidl new file mode 100644 index 000000000000..bdfb2bad7ab4 --- /dev/null +++ b/core/java/android/app/ISearchManagerCallback.aidl @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +/** @hide */ +oneway interface ISearchManagerCallback { + void onDismiss(); + void onCancel(); +} diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 7de657238bcb..9141c4cf6f56 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -76,8 +76,8 @@ import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicLong; /** - * In-application-process implementation of Search Bar. This is still controlled by the - * SearchManager, but it runs in the current activity's process to keep things lighter weight. + * System search dialog. This is controlled by the + * SearchManagerService and runs in the system process. * * @hide */ @@ -179,17 +179,17 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Window theWindow = getWindow(); - theWindow.setGravity(Gravity.TOP|Gravity.FILL_HORIZONTAL); - setContentView(com.android.internal.R.layout.search_bar); - theWindow.setLayout(ViewGroup.LayoutParams.FILL_PARENT, - // taking up the whole window (even when transparent) is less than ideal, - // but necessary to show the popup window until the window manager supports - // having windows anchored by their parent but not clipped by them. - ViewGroup.LayoutParams.FILL_PARENT); + Window theWindow = getWindow(); WindowManager.LayoutParams lp = theWindow.getAttributes(); + lp.type = WindowManager.LayoutParams.TYPE_SEARCH_BAR; + lp.width = ViewGroup.LayoutParams.FILL_PARENT; + // taking up the whole window (even when transparent) is less than ideal, + // but necessary to show the popup window until the window manager supports + // having windows anchored by their parent but not clipped by them. + lp.height = ViewGroup.LayoutParams.FILL_PARENT; + lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; theWindow.setAttributes(lp); @@ -234,10 +234,12 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // Save voice intent for later queries/launching mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); + mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH); mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); + mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mLocationManager = (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE); @@ -278,12 +280,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS */ public boolean show(String initialQuery, boolean selectInitialQuery, ComponentName componentName, Bundle appSearchData, boolean globalSearch) { - if (isShowing()) { - // race condition - already showing but not handling events yet. - // in this case, just discard the "show" request - return true; - } - + // Reset any stored values from last time dialog was shown. mStoredComponentName = null; mStoredAppSearchData = null; @@ -442,11 +439,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS stopLocationUpdates(); - // TODO: Removing the listeners means that they never get called, since - // Dialog.dismissDialog() calls onStop() before sendDismissMessage(). - setOnCancelListener(null); - setOnDismissListener(null); - // stop receiving broadcasts (throws exception if none registered) try { getContext().unregisterReceiver(mBroadcastReceiver); @@ -654,15 +646,15 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mSearchAutoComplete.setDropDownAnimationStyle(0); // no animation mSearchAutoComplete.setThreshold(mSearchable.getSuggestThreshold()); + // we dismiss the entire dialog instead + mSearchAutoComplete.setDropDownDismissedOnCompletion(false); if (mGlobalSearchMode) { mSearchAutoComplete.setDropDownAlwaysVisible(true); // fill space until results come in - mSearchAutoComplete.setDropDownDismissedOnCompletion(false); mSearchAutoComplete.setDropDownBackgroundResource( com.android.internal.R.drawable.search_dropdown_background); } else { mSearchAutoComplete.setDropDownAlwaysVisible(false); - mSearchAutoComplete.setDropDownDismissedOnCompletion(true); mSearchAutoComplete.setDropDownBackgroundResource( com.android.internal.R.drawable.search_dropdown_background_apps); } @@ -1317,7 +1309,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** - * Launches an intent. Also dismisses the search dialog if not in global search mode. + * Launches an intent and dismisses the search dialog (unless the intent + * is one of the special intents that modifies the state of the search dialog). */ private void launchIntent(Intent intent) { if (intent == null) { @@ -1326,9 +1319,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (handleSpecialIntent(intent)){ return; } - if (!mGlobalSearchMode) { - dismiss(); - } + dismiss(); getContext().startActivity(intent); } @@ -1511,6 +1502,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS int actionKey, String actionMsg) { // Now build the Intent Intent intent = new Intent(action); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (data != null) { intent.setData(data); } @@ -1595,14 +1587,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private boolean isEmpty() { return TextUtils.getTrimmedLength(getText()) == 0; } - - /** - * Clears the entered text. - */ - private void clear() { - setText(""); - } - + /** * We override this method to avoid replacing the query box text * when a suggestion is clicked. diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 820f192cd12d..1ddd20a40327 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -28,6 +28,7 @@ import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.server.search.SearchableInfo; +import android.util.Log; import android.view.KeyEvent; import java.util.List; @@ -1108,6 +1109,10 @@ import java.util.List; public class SearchManager implements DialogInterface.OnDismissListener, DialogInterface.OnCancelListener { + + private static final boolean DBG = false; + private static final String TAG = "SearchManager"; + /** * This is a shortcut definition for the default menu key to use for invoking search. * @@ -1494,12 +1499,14 @@ public class SearchManager private static ISearchManager sService = getSearchManagerService(); private final Context mContext; - private final Handler mHandler; - - private SearchDialog mSearchDialog; - - private OnDismissListener mDismissListener = null; - private OnCancelListener mCancelListener = null; + + // package private since they are used by the inner class SearchManagerCallback + /* package */ boolean mIsShowing = false; + /* package */ final Handler mHandler; + /* package */ OnDismissListener mDismissListener = null; + /* package */ OnCancelListener mCancelListener = null; + + private final SearchManagerCallback mSearchManagerCallback = new SearchManagerCallback(); /*package*/ SearchManager(Context context, Handler handler) { mContext = context; @@ -1551,17 +1558,16 @@ public class SearchManager ComponentName launchActivity, Bundle appSearchData, boolean globalSearch) { - - if (mSearchDialog == null) { - mSearchDialog = new SearchDialog(mContext); + if (DBG) debug("startSearch(), mIsShowing=" + mIsShowing); + if (mIsShowing) return; + try { + mIsShowing = true; + // activate the search manager and start it up! + sService.startSearch(initialQuery, selectInitialQuery, launchActivity, appSearchData, + globalSearch, mSearchManagerCallback); + } catch (RemoteException ex) { + Log.e(TAG, "startSearch() failed: " + ex); } - - // activate the search manager and start it up! - mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData, - globalSearch); - - mSearchDialog.setOnCancelListener(this); - mSearchDialog.setOnDismissListener(this); } /** @@ -1575,9 +1581,16 @@ public class SearchManager * * @see #startSearch */ - public void stopSearch() { - if (mSearchDialog != null) { - mSearchDialog.cancel(); + public void stopSearch() { + if (DBG) debug("stopSearch(), mIsShowing=" + mIsShowing); + if (!mIsShowing) return; + try { + sService.stopSearch(); + // onDismiss will also clear this, but we do it here too since onDismiss() is + // called asynchronously. + mIsShowing = false; + } catch (RemoteException ex) { + Log.e(TAG, "stopSearch() failed: " + ex); } } @@ -1590,13 +1603,11 @@ public class SearchManager * * @hide */ - public boolean isVisible() { - if (mSearchDialog != null) { - return mSearchDialog.isShowing(); - } - return false; + public boolean isVisible() { + if (DBG) debug("isVisible(), mIsShowing=" + mIsShowing); + return mIsShowing; } - + /** * See {@link SearchManager#setOnDismissListener} for configuring your activity to monitor * search UI state. @@ -1631,79 +1642,112 @@ public class SearchManager public void setOnDismissListener(final OnDismissListener listener) { mDismissListener = listener; } - - /** - * The callback from the search dialog when dismissed - * @hide - */ - public void onDismiss(DialogInterface dialog) { - if (dialog == mSearchDialog) { - if (mDismissListener != null) { - mDismissListener.onDismiss(); - } - } - } /** * Set or clear the callback that will be invoked whenever the search UI is canceled. * * @param listener The {@link OnCancelListener} to use, or null. */ - public void setOnCancelListener(final OnCancelListener listener) { + public void setOnCancelListener(OnCancelListener listener) { mCancelListener = listener; } - - - /** - * The callback from the search dialog when canceled - * @hide - */ - public void onCancel(DialogInterface dialog) { - if (dialog == mSearchDialog) { - if (mCancelListener != null) { - mCancelListener.onCancel(); + + private class SearchManagerCallback extends ISearchManagerCallback.Stub { + + private final Runnable mFireOnDismiss = new Runnable() { + public void run() { + if (DBG) debug("mFireOnDismiss"); + mIsShowing = false; + if (mDismissListener != null) { + mDismissListener.onDismiss(); + } + } + }; + + private final Runnable mFireOnCancel = new Runnable() { + public void run() { + if (DBG) debug("mFireOnCancel"); + // doesn't need to clear mIsShowing since onDismiss() always gets called too + if (mCancelListener != null) { + mCancelListener.onCancel(); + } } + }; + + public void onDismiss() { + if (DBG) debug("onDismiss()"); + mHandler.post(mFireOnDismiss); + } + + public void onCancel() { + if (DBG) debug("onCancel()"); + mHandler.post(mFireOnCancel); } + + } + + // TODO: remove the DialogInterface interfaces from SearchManager. + // This changes the public API, so I'll do it in a separate change. + public void onCancel(DialogInterface dialog) { + throw new UnsupportedOperationException(); + } + public void onDismiss(DialogInterface dialog) { + throw new UnsupportedOperationException(); } /** - * Save instance state so we can recreate after a rotation. - * + * 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 */ - void saveSearchDialog(Bundle outState, String key) { - if (mSearchDialog != null && mSearchDialog.isShowing()) { - Bundle searchDialogState = mSearchDialog.onSaveInstanceState(); - outState.putBundle(key, searchDialogState); + public Bundle saveSearchDialog() { + if (DBG) debug("saveSearchDialog(), mIsShowing=" + mIsShowing); + if (!mIsShowing) return null; + try { + return sService.onSaveInstanceState(); + } catch (RemoteException ex) { + Log.e(TAG, "onSaveInstanceState() failed: " + ex); + return null; } } /** - * Restore instance state after a rotation. - * + * Restores the state of the search dialog. + * + * @param searchDialogState Bundle to read the state from. + * * @hide */ - void restoreSearchDialog(Bundle inState, String key) { - Bundle searchDialogState = inState.getBundle(key); - if (searchDialogState != null) { - if (mSearchDialog == null) { - mSearchDialog = new SearchDialog(mContext); - } - mSearchDialog.onRestoreInstanceState(searchDialogState); + public void restoreSearchDialog(Bundle searchDialogState) { + if (DBG) debug("restoreSearchDialog(" + searchDialogState + ")"); + if (searchDialogState == null) return; + try { + sService.onRestoreInstanceState(searchDialogState); + } catch (RemoteException ex) { + Log.e(TAG, "onRestoreInstanceState() failed: " + ex); } } - + /** - * Hook for updating layout on a rotation - * + * Update the search dialog after a configuration change. + * + * @param newConfig The new configuration. + * * @hide */ - void onConfigurationChanged(Configuration newConfig) { - if (mSearchDialog != null && mSearchDialog.isShowing()) { - mSearchDialog.onConfigurationChanged(newConfig); + public void onConfigurationChanged(Configuration newConfig) { + if (DBG) debug("onConfigurationChanged(" + newConfig + "), mIsShowing=" + mIsShowing); + if (!mIsShowing) return; + try { + sService.onConfigurationChanged(newConfig); + } catch (RemoteException ex) { + Log.e(TAG, "onConfigurationChanged() failed:" + ex); } } - + private static ISearchManager getSearchManagerService() { return ISearchManager.Stub.asInterface( ServiceManager.getService(Context.SEARCH_SERVICE)); @@ -1724,7 +1768,8 @@ public class SearchManager boolean globalSearch) { try { return sService.getSearchableInfo(componentName, globalSearch); - } catch (RemoteException e) { + } catch (RemoteException ex) { + Log.e(TAG, "getSearchableInfo() failed: " + ex); return null; } } @@ -1805,6 +1850,7 @@ public class SearchManager try { return sService.getSearchablesInGlobalSearch(); } catch (RemoteException e) { + Log.e(TAG, "getSearchablesInGlobalSearch() failed: " + e); return null; } } @@ -1812,7 +1858,8 @@ public class SearchManager /** * Returns a list of the searchable activities that handle web searches. * - * @return a a list of all searchable activities that handle {@link SearchManager#ACTION_WEB_SEARCH}. + * @return a list of all searchable activities that handle + * {@link android.content.Intent#ACTION_WEB_SEARCH}. * * @hide because SearchableInfo is not part of the API. */ @@ -1820,6 +1867,7 @@ public class SearchManager try { return sService.getSearchablesForWebSearch(); } catch (RemoteException e) { + Log.e(TAG, "getSearchablesForWebSearch() failed: " + e); return null; } } @@ -1835,6 +1883,7 @@ public class SearchManager try { return sService.getDefaultSearchableForWebSearch(); } catch (RemoteException e) { + Log.e(TAG, "getDefaultSearchableForWebSearch() failed: " + e); return null; } } @@ -1850,6 +1899,12 @@ public class SearchManager try { sService.setDefaultWebSearch(component); } catch (RemoteException e) { + Log.e(TAG, "setDefaultWebSearch() failed: " + e); } } + + private static void debug(String msg) { + Thread thread = Thread.currentThread(); + Log.d(TAG, msg + " (" + thread.getName() + "-" + thread.getId() + ")"); + } } diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java index 060bcea31b7e..db812d1f66db 100644 --- a/core/java/android/server/search/SearchManagerService.java +++ b/core/java/android/server/search/SearchManagerService.java @@ -17,15 +17,25 @@ package android.server.search; import android.app.ISearchManager; +import android.app.ISearchManagerCallback; +import android.app.SearchDialog; +import android.app.SearchManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Configuration; +import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; +import android.util.Log; import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; /** * This is a simplified version of the Search Manager service. It no longer handles @@ -34,16 +44,20 @@ import java.util.List; * invoked search) to specific searchable activities (where the search will be dispatched). */ public class SearchManagerService extends ISearchManager.Stub + implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { // general debugging support private static final String TAG = "SearchManagerService"; - private static final boolean DEBUG = false; + private static final boolean DBG = false; // class maintenance and general shared data private final Context mContext; private final Handler mHandler; private boolean mSearchablesDirty; - private Searchables mSearchables; + private final Searchables mSearchables; + + final SearchDialog mSearchDialog; + ISearchManagerCallback mCallback = null; /** * Initializes the Search Manager service in the provided system context. @@ -56,6 +70,9 @@ public class SearchManagerService extends ISearchManager.Stub mHandler = new Handler(); mSearchablesDirty = true; mSearchables = new Searchables(context); + mSearchDialog = new SearchDialog(context); + mSearchDialog.setOnCancelListener(this); + mSearchDialog.setOnDismissListener(this); // Setup the infrastructure for updating and maintaining the list // of searchable activities. @@ -107,6 +124,7 @@ public class SearchManagerService extends ISearchManager.Stub * a package add/remove broadcast message. */ private void updateSearchables() { + if (DBG) debug("updateSearchables()"); mSearchables.buildSearchableList(); mSearchablesDirty = false; } @@ -137,6 +155,10 @@ public class SearchManagerService extends ISearchManager.Stub if (globalSearch) { si = mSearchables.getDefaultSearchable(); } else { + if (launchActivity == null) { + Log.e(TAG, "getSearchableInfo(), activity == null"); + return null; + } si = mSearchables.getSearchableInfo(launchActivity); } @@ -150,6 +172,145 @@ public class SearchManagerService extends ISearchManager.Stub updateSearchablesIfDirty(); return mSearchables.getSearchablesInGlobalSearchList(); } + /** + * Launches the search UI on the main thread of the service. + * + * @see SearchManager#startSearch(String, boolean, ComponentName, Bundle, boolean) + */ + public void startSearch(final String initialQuery, + final boolean selectInitialQuery, + final ComponentName launchActivity, + final Bundle appSearchData, + final boolean globalSearch, + final ISearchManagerCallback searchManagerCallback) { + if (DBG) debug("startSearch()"); + Runnable task = new Runnable() { + public void run() { + performStartSearch(initialQuery, + selectInitialQuery, + launchActivity, + appSearchData, + globalSearch, + searchManagerCallback); + } + }; + mHandler.post(task); + } + + /** + * Actually launches the search. This must be called on the service UI thread. + */ + /*package*/ void performStartSearch(String initialQuery, + boolean selectInitialQuery, + ComponentName launchActivity, + Bundle appSearchData, + boolean globalSearch, + ISearchManagerCallback searchManagerCallback) { + if (DBG) debug("performStartSearch()"); + mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData, + globalSearch); + if (searchManagerCallback != null) { + mCallback = searchManagerCallback; + } + } + + /** + * Cancels the search dialog. Can be called from any thread. + */ + public void stopSearch() { + if (DBG) debug("stopSearch()"); + mHandler.post(new Runnable() { + public void run() { + performStopSearch(); + } + }); + } + + /** + * Cancels the search dialog. Must be called from the service UI thread. + */ + /*package*/ void performStopSearch() { + if (DBG) debug("performStopSearch()"); + mSearchDialog.cancel(); + } + + /** + * Determines if the Search UI is currently displayed. + * + * @see SearchManager#isVisible() + */ + public boolean isVisible() { + return postAndWait(mIsShowing, false, "isShowing()"); + } + + private final Callable<Boolean> mIsShowing = new Callable<Boolean>() { + public Boolean call() { + return mSearchDialog.isShowing(); + } + }; + + public Bundle onSaveInstanceState() { + return postAndWait(mOnSaveInstanceState, null, "onSaveInstanceState()"); + } + + private final Callable<Bundle> mOnSaveInstanceState = new Callable<Bundle>() { + public Bundle call() { + if (mSearchDialog.isShowing()) { + return mSearchDialog.onSaveInstanceState(); + } else { + return null; + } + } + }; + + public void onRestoreInstanceState(final Bundle searchDialogState) { + if (searchDialogState != null) { + mHandler.post(new Runnable() { + public void run() { + mSearchDialog.onRestoreInstanceState(searchDialogState); + } + }); + } + } + + public void onConfigurationChanged(final Configuration newConfig) { + mHandler.post(new Runnable() { + public void run() { + if (mSearchDialog.isShowing()) { + mSearchDialog.onConfigurationChanged(newConfig); + } + } + }); + } + + /** + * Called by {@link SearchDialog} when it goes away. + */ + public void onDismiss(DialogInterface dialog) { + if (DBG) debug("onDismiss()"); + if (mCallback != null) { + try { + mCallback.onDismiss(); + } catch (RemoteException ex) { + Log.e(TAG, "onDismiss() failed: " + ex); + } + } + } + + /** + * Called by {@link SearchDialog} when the user or activity cancels search. + * When this is called, {@link #onDismiss} is called too. + */ + public void onCancel(DialogInterface dialog) { + if (DBG) debug("onCancel()"); + if (mCallback != null) { + try { + mCallback.onCancel(); + } catch (RemoteException ex) { + Log.e(TAG, "onCancel() failed: " + ex); + } + } + } /** * Returns a list of the searchable activities that handle web searches. @@ -173,4 +334,34 @@ public class SearchManagerService extends ISearchManager.Stub public void setDefaultWebSearch(ComponentName component) { mSearchables.setDefaultWebSearch(component); } + + /** + * Runs an operation on the handler for the service, blocks until it returns, + * and returns the value returned by the operation. + * + * @param <V> Return value type. + * @param callable Operation to run. + * @param errorResult Value to return if the operations throws an exception. + * @param name Operation name to include in error log messages. + * @return The value returned by the operation. + */ + private <V> V postAndWait(Callable<V> callable, V errorResult, String name) { + FutureTask<V> task = new FutureTask<V>(callable); + mHandler.post(task); + try { + return task.get(); + } catch (InterruptedException ex) { + Log.e(TAG, "Error calling " + name + ": " + ex); + return errorResult; + } catch (ExecutionException ex) { + Log.e(TAG, "Error calling " + name + ": " + ex); + return errorResult; + } + } + + private static void debug(String msg) { + Thread thread = Thread.currentThread(); + Log.d(TAG, msg + " (" + thread.getName() + "-" + thread.getId() + ")"); + } + } diff --git a/core/java/android/server/search/SearchableInfo.java b/core/java/android/server/search/SearchableInfo.java index 4df736814151..90dfa0b84689 100644 --- a/core/java/android/server/search/SearchableInfo.java +++ b/core/java/android/server/search/SearchableInfo.java @@ -320,7 +320,7 @@ public final class SearchableInfo implements Parcelable { // for now, implement some form of rules - minimal data if (mLabelId == 0) { - throw new IllegalArgumentException("No label."); + throw new IllegalArgumentException("Search label must be a resource reference."); } } @@ -441,13 +441,17 @@ public final class SearchableInfo implements Parcelable { xml.close(); if (DBG) { - Log.d(LOG_TAG, "Checked " + activityInfo.name - + ",label=" + searchable.getLabelId() - + ",icon=" + searchable.getIconId() - + ",suggestAuthority=" + searchable.getSuggestAuthority() - + ",target=" + searchable.getSearchActivity().getClassName() - + ",global=" + searchable.shouldIncludeInGlobalSearch() - + ",threshold=" + searchable.getSuggestThreshold()); + if (searchable != null) { + Log.d(LOG_TAG, "Checked " + activityInfo.name + + ",label=" + searchable.getLabelId() + + ",icon=" + searchable.getIconId() + + ",suggestAuthority=" + searchable.getSuggestAuthority() + + ",target=" + searchable.getSearchActivity().getClassName() + + ",global=" + searchable.shouldIncludeInGlobalSearch() + + ",threshold=" + searchable.getSuggestThreshold()); + } else { + Log.d(LOG_TAG, "Checked " + activityInfo.name + ", no searchable meta-data"); + } } return searchable; } diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index 1b30aa0a218c..367b26ce45d3 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -59,9 +59,15 @@ public final class Formatter { result = result / 1024; } if (result < 100) { - return String.format("%.2f%s", result, context.getText(suffix).toString()); + String value = String.format("%.2f", result); + return context.getResources(). + getString(com.android.internal.R.string.fileSizeSuffix, + value, context.getString(suffix)); } - return String.format("%.0f%s", result, context.getText(suffix).toString()); + String value = String.format("%.0f", result); + return context.getResources(). + getString(com.android.internal.R.string.fileSizeSuffix, + value, context.getString(suffix)); } /** diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java index 2a84683cdc86..77393009db05 100644 --- a/core/java/android/webkit/JWebCoreJavaBridge.java +++ b/core/java/android/webkit/JWebCoreJavaBridge.java @@ -186,6 +186,21 @@ final class JWebCoreJavaBridge extends Handler { mHasInstantTimer = false; } + private String[] getKeyStrengthList() { + // FIXME: fake the list for now + String[] list = new String[2]; + list[0] = "1024"; + list[1] = "512"; + return list; + } + + private String getSignedPublicKey(int index, String challenge, String url) { + // FIXME: do nothing for now + Log.w(LOGTAG, "getSignedPublicKey for " + index + " and challenge=" + + challenge + " and url=" + url); + return ""; + } + private native void nativeConstructor(); private native void nativeFinalize(); private native void sharedTimerFired(); diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index e85b14cf98da..0011c45cc0b6 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -21,6 +21,7 @@ <string name="gigabyteShort">"Go"</string> <string name="terabyteShort">"To"</string> <string name="petabyteShort">"Po"</string> + <string name="fileSizeSuffix"><xliff:g id="number" example="123">%1$s</xliff:g> <xliff:g id="unit" example="KB">%2$s</xliff:g></string> <string name="untitled">"<sans titre>"</string> <string name="ellipsis">"…"</string> <string name="emptyPhoneNumber">"(Aucun numéro de téléphone)"</string> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 79dc1baa7241..6f5b29adf5dd 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -30,6 +30,11 @@ <string name="terabyteShort">TB</string> <!-- Suffix added to a number to signify size in petabytes. --> <string name="petabyteShort">PB</string> + <!-- Format string used to add a suffix like "KB" or "MB" to a number + to display a size in kilobytes, megabytes, or other size units. + Some languages (like French) will want to add a space between + the placeholders. --> + <string name="fileSizeSuffix"><xliff:g id="number" example="123">%1$s</xliff:g><xliff:g id="unit" example="KB">%2$s</xliff:g></string> <!-- Used in Contacts for a field that has no label and in Note Pad for a note with no name. --> diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnService.java b/packages/VpnServices/src/com/android/server/vpn/VpnService.java index 01106b36c3ad..fdefe9cf7890 100644 --- a/packages/VpnServices/src/com/android/server/vpn/VpnService.java +++ b/packages/VpnServices/src/com/android/server/vpn/VpnService.java @@ -20,8 +20,8 @@ import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; -import android.net.vpn.SingleServerProfile; import android.net.vpn.VpnManager; +import android.net.vpn.VpnProfile; import android.net.vpn.VpnState; import android.os.FileObserver; import android.os.SystemProperties; @@ -38,7 +38,7 @@ import java.util.List; /** * The service base class for managing a type of VPN connection. */ -abstract class VpnService<E extends SingleServerProfile> { +abstract class VpnService<E extends VpnProfile> { private static final int NOTIFICATION_ID = 1; private static final String PROFILES_ROOT = VpnManager.PROFILES_PATH + "/"; public static final String DEFAULT_CONFIG_PATH = "/etc"; diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml index fd6e6d8ec3d6..55d4d64bf547 100644 --- a/tests/AndroidTests/AndroidManifest.xml +++ b/tests/AndroidTests/AndroidManifest.xml @@ -219,7 +219,20 @@ </service> <!-- Application components used for search manager tests --> - <!-- TODO: Removed temporarily - need to be replaced using mocks --> + + <activity android:name=".SearchableActivity" + android:label="Searchable Activity"> + <intent-filter> + <action android:name="android.intent.action.SEARCH" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + <meta-data android:name="android.app.searchable" + android:resource="@xml/searchable" /> + </activity> + + <provider android:name=".SuggestionProvider" + android:authorities="com.android.unit_tests.SuggestionProvider"> + </provider> <!-- Used to test IPC. --> <service android:name=".binder.BinderTestService" diff --git a/tests/AndroidTests/res/values/strings.xml b/tests/AndroidTests/res/values/strings.xml index 21c72cf8b93f..49d8ae72d964 100644 --- a/tests/AndroidTests/res/values/strings.xml +++ b/tests/AndroidTests/res/values/strings.xml @@ -50,5 +50,8 @@ <item quantity="other">Some dogs</item> </plurals> + <string name="searchable_label">SearchManager Test</string> + <string name="searchable_hint">A search hint</string> + <!-- <string name="layout_six_text_text">F</string> --> </resources> diff --git a/tests/AndroidTests/res/xml/searchable.xml b/tests/AndroidTests/res/xml/searchable.xml index a40d53de0511..9d293b5ac425 100644 --- a/tests/AndroidTests/res/xml/searchable.xml +++ b/tests/AndroidTests/res/xml/searchable.xml @@ -15,7 +15,12 @@ --> <searchable xmlns:android="http://schemas.android.com/apk/res/android" - android:label="SearchManagerTest" - android:hint="SearchManagerTest Hint" -/> + android:label="@string/searchable_label" + android:hint="@string/searchable_hint" + android:searchSuggestAuthority="com.android.unit_tests.SuggestionProvider" + > + <actionkey android:keycode="KEYCODE_CALL" + android:suggestActionMsgColumn="suggest_action_msg_call" /> + +</searchable>
\ No newline at end of file diff --git a/tests/AndroidTests/src/com/android/unit_tests/SearchManagerTest.java b/tests/AndroidTests/src/com/android/unit_tests/SearchManagerTest.java index f3c15422073b..f03a779b2d01 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/SearchManagerTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/SearchManagerTest.java @@ -23,7 +23,10 @@ import android.app.ISearchManager; import android.app.SearchManager; import android.content.ComponentName; import android.content.Context; +import android.os.Bundle; +import android.os.RemoteException; import android.os.ServiceManager; +import android.server.search.SearchableInfo; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; @@ -37,12 +40,11 @@ import android.util.AndroidRuntimeException; * com.android.unit_tests/android.test.InstrumentationTestRunner */ public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalActivity> { - - // If non-zero, enable a set of tests that start and stop the search manager. - // This is currently disabled because it's causing an unwanted jump from the unit test - // activity into the contacts activity. We'll put this back after we disable that jump. - private static final int TEST_SEARCH_START = 0; - + + private ComponentName SEARCHABLE_ACTIVITY = + new ComponentName("com.android.unit_tests", + "com.android.unit_tests.SearchableActivity"); + /* * Bug list of test ideas. * @@ -88,7 +90,30 @@ public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalAct super.setUp(); Activity testActivity = getActivity(); - mContext = (Context)testActivity; + mContext = testActivity; + } + + private ISearchManager getSearchManagerService() { + return ISearchManager.Stub.asInterface( + ServiceManager.getService(Context.SEARCH_SERVICE)); + } + + // Checks that the search UI is visible. + private void assertSearchVisible() { + SearchManager searchManager = (SearchManager) + mContext.getSystemService(Context.SEARCH_SERVICE); + assertTrue("SearchManager thinks search UI isn't visible when it should be", + searchManager.isVisible()); + } + + // Checks that the search UI is not visible. + // This checks both the SearchManager and the SearchManagerService, + // since SearchManager keeps a local variable for the visibility. + private void assertSearchNotVisible() { + SearchManager searchManager = (SearchManager) + mContext.getSystemService(Context.SEARCH_SERVICE); + assertFalse("SearchManager thinks search UI is visible when it shouldn't be", + searchManager.isVisible()); } /** @@ -97,9 +122,7 @@ public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalAct */ @MediumTest public void testSearchManagerInterfaceAvailable() { - ISearchManager searchManager1 = ISearchManager.Stub.asInterface( - ServiceManager.getService(Context.SEARCH_SERVICE)); - assertNotNull(searchManager1); + assertNotNull(getSearchManagerService()); } /** @@ -135,38 +158,127 @@ public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalAct SearchManager searchManager2 = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); assertNotNull(searchManager2); - assertSame( searchManager1, searchManager2 ); + assertSame(searchManager1, searchManager2 ); } - + + @MediumTest + public void testSearchables() { + SearchableInfo si; + + si = SearchManager.getSearchableInfo(SEARCHABLE_ACTIVITY, false); + assertNotNull(si); + assertFalse(SearchManager.isDefaultSearchable(si)); + si = SearchManager.getSearchableInfo(SEARCHABLE_ACTIVITY, true); + assertNotNull(si); + assertTrue(SearchManager.isDefaultSearchable(si)); + si = SearchManager.getSearchableInfo(null, true); + assertNotNull(si); + assertTrue(SearchManager.isDefaultSearchable(si)); + } + + /** + * Tests that rapid calls to start-stop-start doesn't cause problems. + */ + @MediumTest + public void testSearchManagerFastInvocations() throws Exception { + SearchManager searchManager = (SearchManager) + mContext.getSystemService(Context.SEARCH_SERVICE); + assertNotNull(searchManager); + assertSearchNotVisible(); + + searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false); + assertSearchVisible(); + searchManager.stopSearch(); + searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false); + searchManager.stopSearch(); + assertSearchNotVisible(); + } + + /** + * Tests that startSearch() is idempotent. + */ + @MediumTest + public void testStartSearchIdempotent() throws Exception { + SearchManager searchManager = (SearchManager) + mContext.getSystemService(Context.SEARCH_SERVICE); + assertNotNull(searchManager); + assertSearchNotVisible(); + + searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false); + searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false); + assertSearchVisible(); + searchManager.stopSearch(); + assertSearchNotVisible(); + } + + /** + * Tests that stopSearch() is idempotent and can be called when the search UI is not visible. + */ + @MediumTest + public void testStopSearchIdempotent() throws Exception { + SearchManager searchManager = (SearchManager) + mContext.getSystemService(Context.SEARCH_SERVICE); + assertNotNull(searchManager); + assertSearchNotVisible(); + searchManager.stopSearch(); + assertSearchNotVisible(); + + searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false); + assertSearchVisible(); + searchManager.stopSearch(); + searchManager.stopSearch(); + assertSearchNotVisible(); + } + /** * The goal of this test is to confirm that we can start and then * stop a simple search. */ - - @MediumTest - public void testSearchManagerInvocations() { + @MediumTest + public void testSearchManagerInvocations() throws Exception { SearchManager searchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); assertNotNull(searchManager); - - // TODO: make a real component name, or remove this need - final ComponentName cn = new ComponentName("", ""); - - if (TEST_SEARCH_START != 0) { - // These tests should simply run to completion w/o exceptions - searchManager.startSearch(null, false, cn, null, false); - searchManager.stopSearch(); - - searchManager.startSearch("", false, cn, null, false); - searchManager.stopSearch(); - - searchManager.startSearch("test search string", false, cn, null, false); - searchManager.stopSearch(); - - searchManager.startSearch("test search string", true, cn, null, false); - searchManager.stopSearch(); - } - } + assertSearchNotVisible(); -} + // These tests should simply run to completion w/o exceptions + searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false); + assertSearchVisible(); + searchManager.stopSearch(); + assertSearchNotVisible(); + + searchManager.startSearch("", false, SEARCHABLE_ACTIVITY, null, false); + assertSearchVisible(); + searchManager.stopSearch(); + assertSearchNotVisible(); + + searchManager.startSearch("test search string", false, SEARCHABLE_ACTIVITY, null, false); + assertSearchVisible(); + searchManager.stopSearch(); + assertSearchNotVisible(); + + searchManager.startSearch("test search string", true, SEARCHABLE_ACTIVITY, null, false); + assertSearchVisible(); + searchManager.stopSearch(); + assertSearchNotVisible(); + } + @MediumTest + public void testSearchDialogState() throws Exception { + SearchManager searchManager = (SearchManager) + mContext.getSystemService(Context.SEARCH_SERVICE); + assertNotNull(searchManager); + + Bundle searchState; + + // search dialog not visible, so no state should be stored + searchState = searchManager.saveSearchDialog(); + assertNull(searchState); + + searchManager.startSearch("test search string", true, SEARCHABLE_ACTIVITY, null, false); + searchState = searchManager.saveSearchDialog(); + assertNotNull(searchState); + searchManager.stopSearch(); + } + +} diff --git a/tests/AndroidTests/src/com/android/unit_tests/SearchableActivity.java b/tests/AndroidTests/src/com/android/unit_tests/SearchableActivity.java new file mode 100644 index 000000000000..53f40e992847 --- /dev/null +++ b/tests/AndroidTests/src/com/android/unit_tests/SearchableActivity.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.unit_tests; + +import android.app.Activity; +import android.os.Bundle; + +public class SearchableActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + finish(); + } + +} diff --git a/tests/AndroidTests/src/com/android/unit_tests/SuggestionProvider.java b/tests/AndroidTests/src/com/android/unit_tests/SuggestionProvider.java new file mode 100644 index 000000000000..bc61e27b10df --- /dev/null +++ b/tests/AndroidTests/src/com/android/unit_tests/SuggestionProvider.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.unit_tests; + +import android.app.SearchManager; +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.Intent; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.net.Uri; + +/** Simple test provider that runs in the local process. + * + * Used by {@link SearchManagerTest}. + */ +public class SuggestionProvider extends ContentProvider { + private static final String TAG = "SuggestionProvider"; + + private static final int SEARCH_SUGGESTIONS = 1; + + private static final UriMatcher sURLMatcher = new UriMatcher( + UriMatcher.NO_MATCH); + + static { + sURLMatcher.addURI("*", SearchManager.SUGGEST_URI_PATH_QUERY, + SEARCH_SUGGESTIONS); + sURLMatcher.addURI("*", SearchManager.SUGGEST_URI_PATH_QUERY + "/*", + SEARCH_SUGGESTIONS); + } + + private static final String[] COLUMNS = new String[] { + "_id", + SearchManager.SUGGEST_COLUMN_TEXT_1, + SearchManager.SUGGEST_COLUMN_INTENT_ACTION, + SearchManager.SUGGEST_COLUMN_QUERY + }; + + public SuggestionProvider() { + } + + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor query(Uri url, String[] projectionIn, String selection, + String[] selectionArgs, String sort) { + int match = sURLMatcher.match(url); + switch (match) { + case SEARCH_SUGGESTIONS: + String query = url.getLastPathSegment(); + MatrixCursor cursor = new MatrixCursor(COLUMNS); + String[] suffixes = { "", "a", " foo", "XXXXXXXXXXXXXXXXX" }; + for (String suffix : suffixes) { + addRow(cursor, query + suffix); + } + return cursor; + default: + throw new IllegalArgumentException("Unknown URL: " + url); + } + } + + private void addRow(MatrixCursor cursor, String string) { + long id = cursor.getCount(); + cursor.newRow().add(id).add(string).add(Intent.ACTION_SEARCH).add(string); + } + + @Override + public String getType(Uri url) { + int match = sURLMatcher.match(url); + switch (match) { + case SEARCH_SUGGESTIONS: + return SearchManager.SUGGEST_MIME_TYPE; + default: + throw new IllegalArgumentException("Unknown URL: " + url); + } + } + + @Override + public int update(Uri url, ContentValues values, String where, String[] whereArgs) { + throw new UnsupportedOperationException("update not supported"); + } + + @Override + public Uri insert(Uri url, ContentValues initialValues) { + throw new UnsupportedOperationException("insert not supported"); + } + + @Override + public int delete(Uri url, String where, String[] whereArgs) { + throw new UnsupportedOperationException("delete not supported"); + } +} diff --git a/tests/DumpRenderTree/assets/run_layout_tests.py b/tests/DumpRenderTree/assets/run_layout_tests.py index 5409a0cdb0e3..50ccb24c6677 100755 --- a/tests/DumpRenderTree/assets/run_layout_tests.py +++ b/tests/DumpRenderTree/assets/run_layout_tests.py @@ -61,7 +61,8 @@ def DumpRenderTreeFinished(adb_cmd): adb_output = subprocess.Popen(shell_cmd_str, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] return adb_output.strip() == "#DONE" -def DiffResults(marker, new_results, old_results, diff_results, strip_reason): +def DiffResults(marker, new_results, old_results, diff_results, strip_reason, + new_count_first=True): """ Given two result files, generate diff and write to diff_results file. All arguments are absolute paths to files. @@ -85,21 +86,25 @@ def DiffResults(marker, new_results, old_results, diff_results, strip_reason): for i in range(0, len(cdict)): cdict[i] = cdict[i].split(' ')[0] + "\n" - # Find results in new_results missing in old_results - new_count=0 - for line in ndict: - if line not in cdict: - diff_file.writelines("+ " + line) - new_count += 1 - - # Find results in old_results missing in new_results - missing_count=0 - for line in cdict: - if line not in ndict: - diff_file.writelines("- " + line) - missing_count += 1 - - logging.info(marker + " >>> " + str(new_count) + " new, " + str(missing_count) + " misses") + params = { + "new": [0, ndict, cdict, "+"], + "miss": [0, cdict, ndict, "-"] + } + if new_count_first: + order = ["new", "miss"] + else: + order = ["miss", "new"] + + for key in order: + for line in params[key][1]: + if line not in params[key][2]: + if line[-1] != "\n": + line += "\n"; + diff_file.writelines(params[key][3] + line) + params[key][0] += 1 + + logging.info(marker + " >>> " + str(params["new"][0]) + " new, " + + str(params["miss"][0]) + " misses") diff_file.writelines("\n\n") @@ -121,12 +126,12 @@ def CompareResults(ref_dir, results_dir): if os.path.exists(diff_result): os.remove(diff_result) - files=["passed", "failed", "nontext", "crashed"] + files=["crashed", "failed", "passed", "nontext"] for f in files: result_file_name = "layout_tests_" + f + ".txt" DiffResults(f, os.path.join(results_dir, result_file_name), os.path.join(ref_dir, result_file_name), diff_result, - f == "failed") + False, files != "passed") logging.info("Detailed diffs are in " + diff_result) def main(options, args): diff --git a/tests/DumpRenderTree/assets/run_reliability_tests.py b/tests/DumpRenderTree/assets/run_reliability_tests.py index b038740115df..23f93df8508e 100755 --- a/tests/DumpRenderTree/assets/run_reliability_tests.py +++ b/tests/DumpRenderTree/assets/run_reliability_tests.py @@ -14,6 +14,7 @@ import os import subprocess import sys import time +from Numeric import * TEST_LIST_FILE = "/sdcard/android/reliability_tests_list.txt" TEST_STATUS_FILE = "/sdcard/android/reliability_running_test.txt" @@ -75,21 +76,28 @@ def ProcessPageLoadTime(raw_log): logging.info("Line has more than one '|': " + line) continue if pair[0] not in load_times: - load_times[pair[0]] = [0, 0] + load_times[pair[0]] = [] try: pair[1] = int(pair[1]) except ValueError: logging.info("Lins has non-numeric load time: " + line) continue - load_times[pair[0]][0] += pair[1] - load_times[pair[0]][1] += 1 + load_times[pair[0]].append(pair[1]) log_handle.close() # rewrite the average time to file log_handle = open(raw_log, "w") for url, times in load_times.iteritems(): - log_handle.write("%s|%f\n" % (url, float(times[0]) / times[1])) + # calculate std + arr = array(times) + avg = average(arr) + d = arr - avg + std = sqrt(sum(d * d) / len(arr)) + output = ("%-70s%-10d%-10d%-12.2f%-12.2f%s\n" % + (url, min(arr), max(arr), avg, std, + array2string(arr))) + log_handle.write(output) log_handle.close() @@ -156,6 +164,7 @@ def main(options, args): # clean up previous results RemoveDeviceFile(adb_cmd, TEST_STATUS_FILE) RemoveDeviceFile(adb_cmd, TEST_TIMEOUT_FILE) + RemoveDeviceFile(adb_cmd, TEST_LOAD_TIME_FILE) logging.info("Running the test ...") diff --git a/vpn/java/android/net/vpn/L2tpIpsecProfile.java b/vpn/java/android/net/vpn/L2tpIpsecProfile.java index 893afbe1b024..181619d5a83f 100644 --- a/vpn/java/android/net/vpn/L2tpIpsecProfile.java +++ b/vpn/java/android/net/vpn/L2tpIpsecProfile.java @@ -22,7 +22,7 @@ import android.os.Parcel; * The profile for L2TP-over-IPSec type of VPN. * {@hide} */ -public class L2tpIpsecProfile extends SingleServerProfile { +public class L2tpIpsecProfile extends VpnProfile { private static final long serialVersionUID = 1L; private String mUserCertificate; diff --git a/vpn/java/android/net/vpn/L2tpProfile.java b/vpn/java/android/net/vpn/L2tpProfile.java index 559590f599e5..59d4981be802 100644 --- a/vpn/java/android/net/vpn/L2tpProfile.java +++ b/vpn/java/android/net/vpn/L2tpProfile.java @@ -20,7 +20,7 @@ package android.net.vpn; * The profile for L2TP type of VPN. * {@hide} */ -public class L2tpProfile extends SingleServerProfile { +public class L2tpProfile extends VpnProfile { private static final long serialVersionUID = 1L; @Override diff --git a/vpn/java/android/net/vpn/SingleServerProfile.java b/vpn/java/android/net/vpn/SingleServerProfile.java deleted file mode 100644 index 59b5a7b1e2d1..000000000000 --- a/vpn/java/android/net/vpn/SingleServerProfile.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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.net.vpn; - -import android.os.Parcel; - -/** - * The profile for single-server type of VPN. - * {@hide} - */ -public abstract class SingleServerProfile extends VpnProfile { - private String mServerName; - - public void setServerName(String name) { - mServerName = name; - } - - public String getServerName() { - return mServerName; - } - - @Override - protected void readFromParcel(Parcel in) { - super.readFromParcel(in); - mServerName = in.readString(); - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - super.writeToParcel(parcel, flags); - parcel.writeString(mServerName); - } -} diff --git a/vpn/java/android/net/vpn/VpnProfile.java b/vpn/java/android/net/vpn/VpnProfile.java index 1bfc102ad163..9e24da45a7b0 100644 --- a/vpn/java/android/net/vpn/VpnProfile.java +++ b/vpn/java/android/net/vpn/VpnProfile.java @@ -31,8 +31,10 @@ public abstract class VpnProfile implements Parcelable, Serializable { private static final long serialVersionUID = 1L; private String mName; // unique display name private String mId; // unique identifier + private String mServerName; // VPN server name private String mDomainSuffices; // space separated list private String mRouteList; // space separated list + private String mSavedUsername; private boolean mIsCustomized; private transient VpnState mState = VpnState.IDLE; @@ -58,6 +60,17 @@ public abstract class VpnProfile implements Parcelable, Serializable { } /** + * Sets the name of the VPN server. Used for DNS lookup. + */ + public void setServerName(String name) { + mServerName = name; + } + + public String getServerName() { + return mServerName; + } + + /** * Sets the domain suffices for DNS resolution. * * @param entries a comma-separated list of domain suffices @@ -84,6 +97,14 @@ public abstract class VpnProfile implements Parcelable, Serializable { return mRouteList; } + public void setSavedUsername(String name) { + mSavedUsername = name; + } + + public String getSavedUsername() { + return mSavedUsername; + } + public void setState(VpnState state) { mState = state; } @@ -116,8 +137,10 @@ public abstract class VpnProfile implements Parcelable, Serializable { protected void readFromParcel(Parcel in) { mName = in.readString(); mId = in.readString(); + mServerName = in.readString(); mDomainSuffices = in.readString(); mRouteList = in.readString(); + mSavedUsername = in.readString(); } public static final Parcelable.Creator<VpnProfile> CREATOR = @@ -142,8 +165,10 @@ public abstract class VpnProfile implements Parcelable, Serializable { parcel.writeInt(mIsCustomized ? 1 : 0); parcel.writeString(mName); parcel.writeString(mId); + parcel.writeString(mServerName); parcel.writeString(mDomainSuffices); parcel.writeString(mRouteList); + parcel.writeString(mSavedUsername); } public int describeContents() { |