diff options
author | Jean-Baptiste Queru <jbq@google.com> | 2009-03-18 11:33:14 -0700 |
---|---|---|
committer | Jean-Baptiste Queru <jbq@google.com> | 2009-03-18 11:33:14 -0700 |
commit | 2a73de7b21a89aa2ba4c254d28658b49793425b2 (patch) | |
tree | ded5bcd581464b4174d81c373044b6d36eee58d2 /core/java/android/app | |
parent | 42e48026b21a962e5bf40344d738665ecbd9d74d (diff) | |
parent | ba87e3e6c985e7175152993b5efcc7dd2f0e1c93 (diff) | |
download | frameworks_base-2a73de7b21a89aa2ba4c254d28658b49793425b2.zip frameworks_base-2a73de7b21a89aa2ba4c254d28658b49793425b2.tar.gz frameworks_base-2a73de7b21a89aa2ba4c254d28658b49793425b2.tar.bz2 |
Merge commit 'remotes/korg/cupcake' into merge
Conflicts:
core/java/android/view/animation/TranslateAnimation.java
core/jni/Android.mk
core/res/res/values-en-rGB/strings.xml
libs/audioflinger/AudioFlinger.cpp
libs/surfaceflinger/LayerScreenshot.cpp
packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
Diffstat (limited to 'core/java/android/app')
20 files changed, 1057 insertions, 214 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 4dc4b6a..849a37d 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -53,6 +53,7 @@ import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewManager; import android.view.Window; import android.view.WindowManager; import android.view.ContextMenu.ContextMenuInfo; @@ -93,11 +94,11 @@ import java.util.HashMap; * {@link android.R.styleable#AndroidManifestActivity <activity>} * declaration in their package's <code>AndroidManifest.xml</code>.</p> * - * <p>The Activity class is an important part of an - * <a href="{@docRoot}intro/lifecycle.html">application's overall lifecycle</a>, + * <p>The Activity class is an important part of an application's overall lifecycle, * and the way activities are launched and put together is a fundamental - * part of the platform's - * <a href="{@docRoot}intro/appmodel.html">application model</a>.</p> + * part of the platform's application model. For a detailed perspective on the structure of + * Android applications and lifecycles, please read the <em>Dev Guide</em> document on + * <a href="{@docRoot}guide/topics/fundamentals.html">Application Fundamentals</a>.</p> * * <p>Topics covered here: * <ol> @@ -527,7 +528,7 @@ import java.util.HashMap; * {@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>} * element in their own manifest to be able to start that activity. * - * <p>See the <a href="{@docRoot}devel/security.html">Security Model</a> + * <p>See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> * document for more information on permissions and security in general. * * <a name="ProcessLifecycle"></a> @@ -629,6 +630,9 @@ public class Activity extends ContextThemeWrapper private WindowManager mWindowManager; /*package*/ View mDecor = null; + /*package*/ boolean mWindowAdded = false; + /*package*/ boolean mVisibleFromServer = false; + /*package*/ boolean mVisibleFromClient = true; private CharSequence mTitle; private int mTitleColor = 0; @@ -779,6 +783,8 @@ public class Activity extends ContextThemeWrapper * @see #onPostCreate */ protected void onCreate(Bundle savedInstanceState) { + mVisibleFromClient = mWindow.getWindowStyle().getBoolean( + com.android.internal.R.styleable.Window_windowNoDisplay, true); mCalled = true; } @@ -884,9 +890,9 @@ public class Activity extends ContextThemeWrapper } /** - * Called after {@link #onCreate} or {@link #onStop} when the current - * activity is now being displayed to the user. It will - * be followed by {@link #onRestart}. + * Called after {@link #onCreate} — or after {@link #onRestart} when + * the activity had been stopped, but is now again being displayed to the + * user. It will be followed by {@link #onResume}. * * <p><em>Derived classes must call through to the super class's * implementation of this method. If they do not, an exception will be @@ -901,9 +907,9 @@ public class Activity extends ContextThemeWrapper } /** - * Called after {@link #onStart} when the current activity is being + * Called after {@link #onStop} when the current activity is being * re-displayed to the user (the user has navigated back to it). It will - * be followed by {@link #onResume}. + * be followed by {@link #onStart} and then {@link #onResume}. * * <p>For activities that are using raw {@link Cursor} objects (instead of * creating them through @@ -917,6 +923,7 @@ public class Activity extends ContextThemeWrapper * thrown.</em></p> * * @see #onStop + * @see #onStart * @see #onResume */ protected void onRestart() { @@ -1134,12 +1141,19 @@ public class Activity extends ContextThemeWrapper /** * Called as part of the activity lifecycle when an activity is about to go * into the background as the result of user choice. For example, when the - * user presses the Home key, {@link #onUserLeaving} will be called, but + * user presses the Home key, {@link #onUserLeaveHint} will be called, but * when an incoming phone call causes the in-call Activity to be automatically - * brought to the foreground, {@link #onUserLeaving} will not be called on - * the activity being interrupted. + * brought to the foreground, {@link #onUserLeaveHint} will not be called on + * the activity being interrupted. In cases when it is invoked, this method + * is called right before the activity's {@link #onPause} callback. + * + * <p>This callback and {@link #onUserInteraction} are intended to help + * activities manage status bar notifications intelligently; specifically, + * for helping activities determine the proper time to cancel a notfication. + * + * @see #onUserInteraction() */ - protected void onUserLeaving() { + protected void onUserLeaveHint() { } /** @@ -1207,7 +1221,7 @@ public class Activity extends ContextThemeWrapper /** * Called when you are no longer visible to the user. You will next - * receive either {@link #onStart}, {@link #onDestroy}, or nothing, + * receive either {@link #onRestart}, {@link #onDestroy}, or nothing, * depending on later user activity. * * <p>Note that this method may never be called, in low memory situations @@ -1443,7 +1457,6 @@ public class Activity extends ContextThemeWrapper * @return The Cursor that was returned by query(). * * @see ContentResolver#query(android.net.Uri , String[], String, String[], String) - * @see #managedCommitUpdates * @see #startManagingCursor * @hide */ @@ -1475,7 +1488,6 @@ public class Activity extends ContextThemeWrapper * @return The Cursor that was returned by query(). * * @see ContentResolver#query(android.net.Uri , String[], String, String[], String) - * @see #managedCommitUpdates * @see #startManagingCursor */ public final Cursor managedQuery(Uri uri, @@ -1863,6 +1875,28 @@ public class Activity extends ContextThemeWrapper return false; } + /** + * Called whenever a key, touch, or trackball event is dispatched to the + * activity. Implement this method if you wish to know that the user has + * interacted with the device in some way while your activity is running. + * This callback and {@link #onUserLeaveHint} are intended to help + * activities manage status bar notifications intelligently; specifically, + * for helping activities determine the proper time to cancel a notfication. + * + * <p>All calls to your activity's {@link #onUserLeaveHint} callback will + * be accompanied by calls to {@link #onUserInteraction}. This + * ensures that your activity will be told of relevant user activity such + * as pulling down the notification pane and touching an item there. + * + * <p>Note that this callback will be invoked for the touch down action + * that begins a touch gesture, but may not be invoked for the touch-moved + * and touch-up actions that follow. + * + * @see #onUserLeaveHint() + */ + public void onUserInteraction() { + } + public void onWindowAttributesChanged(WindowManager.LayoutParams params) { // Update window manager if: we have a view, that view is // attached to its parent (which will be a RootView), and @@ -1935,6 +1969,7 @@ public class Activity extends ContextThemeWrapper * @return boolean Return true if this event was consumed. */ public boolean dispatchKeyEvent(KeyEvent event) { + onUserInteraction(); if (getWindow().superDispatchKeyEvent(event)) { return true; } @@ -1952,6 +1987,9 @@ public class Activity extends ContextThemeWrapper * @return boolean Return true if this event was consumed. */ public boolean dispatchTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + onUserInteraction(); + } if (getWindow().superDispatchTouchEvent(ev)) { return true; } @@ -1969,6 +2007,7 @@ public class Activity extends ContextThemeWrapper * @return boolean Return true if this event was consumed. */ public boolean dispatchTrackballEvent(MotionEvent ev) { + onUserInteraction(); if (getWindow().superDispatchTrackballEvent(ev)) { return true; } @@ -2865,6 +2904,35 @@ public class Activity extends ContextThemeWrapper } /** + * Control whether this activity's main window is visible. This is intended + * only for the special case of an activity that is not going to show a + * UI itself, but can't just finish prior to onResume() because it needs + * to wait for a service binding or such. Setting this to false allows + * you to prevent your UI from being shown during that time. + * + * <p>The default value for this is taken from the + * {@link android.R.attr#windowNoDisplay} attribute of the activity's theme. + */ + public void setVisible(boolean visible) { + if (mVisibleFromClient != visible) { + mVisibleFromClient = visible; + if (mVisibleFromServer) { + if (visible) makeVisible(); + else mDecor.setVisibility(View.INVISIBLE); + } + } + } + + void makeVisible() { + if (!mWindowAdded) { + ViewManager wm = getWindowManager(); + wm.addView(mDecor, getWindow().getAttributes()); + mWindowAdded = true; + } + mDecor.setVisibility(View.VISIBLE); + } + + /** * Check to see whether this activity is in the process of finishing, * either because you called {@link #finish} on it or someone else * has requested that it finished. This is often used in @@ -3482,7 +3550,8 @@ public class Activity extends ContextThemeWrapper } final void performUserLeaving() { - onUserLeaving(); + onUserInteraction(); + onUserLeaveHint(); } final void performStop() { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index f9b9221..07520c9d 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -19,16 +19,14 @@ package android.app; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; import android.graphics.Bitmap; import android.os.RemoteException; import android.os.Handler; -import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; -import android.os.Parcelable.Creator; import android.text.TextUtils; -import android.util.Log; import java.util.List; /** @@ -617,7 +615,59 @@ public class ActivityManager { public String pkgList[]; + /** + * Constant for {@link #importance}: this process is running the + * foreground UI. + */ + public static final int IMPORTANCE_FOREGROUND = 100; + + /** + * Constant for {@link #importance}: this process is running something + * that is considered to be actively visible to the user. + */ + public static final int IMPORTANCE_VISIBLE = 200; + + /** + * Constant for {@link #importance}: this process is contains services + * that should remain running. + */ + public static final int IMPORTANCE_SERVICE = 300; + + /** + * Constant for {@link #importance}: this process process contains + * background code that is expendable. + */ + public static final int IMPORTANCE_BACKGROUND = 400; + + /** + * Constant for {@link #importance}: this process is empty of any + * actively running code. + */ + public static final int IMPORTANCE_EMPTY = 500; + + /** + * The relative importance level that the system places on this + * process. May be one of {@link #IMPORTANCE_FOREGROUND}, + * {@link #IMPORTANCE_VISIBLE}, {@link #IMPORTANCE_SERVICE}, + * {@link #IMPORTANCE_BACKGROUND}, or {@link #IMPORTANCE_EMPTY}. These + * constants are numbered so that "more important" values are always + * smaller than "less important" values. + */ + public int importance; + + /** + * An additional ordering within a particular {@link #importance} + * category, providing finer-grained information about the relative + * utility of processes within a category. This number means nothing + * except that a smaller values are more recently used (and thus + * more important). Currently an LRU value is only maintained for + * the {@link #IMPORTANCE_BACKGROUND} category, though others may + * be maintained in the future. + */ + public int lru; + public RunningAppProcessInfo() { + importance = IMPORTANCE_FOREGROUND; } public RunningAppProcessInfo(String pProcessName, int pPid, String pArr[]) { @@ -634,12 +684,16 @@ public class ActivityManager { dest.writeString(processName); dest.writeInt(pid); dest.writeStringArray(pkgList); + dest.writeInt(importance); + dest.writeInt(lru); } public void readFromParcel(Parcel source) { processName = source.readString(); pid = source.readInt(); pkgList = source.readStringArray(); + importance = source.readInt(); + lru = source.readInt(); } public static final Creator<RunningAppProcessInfo> CREATOR = @@ -671,4 +725,37 @@ public class ActivityManager { return null; } } + + /** + * Have the system perform a force stop of everything associated with + * the given application package. All processes that share its uid + * will be killed, all services it has running stopped, all activities + * removed, etc. In addition, a {@link Intent#ACTION_PACKAGE_RESTARTED} + * broadcast will be sent, so that any of its registered alarms can + * be stopped, notifications removed, etc. + * + * <p>You must hold the permission + * {@link android.Manifest.permission#RESTART_PACKAGES} to be able to + * call this method. + * + * @param packageName The name of the package to be stopped. + */ + public void restartPackage(String packageName) { + try { + ActivityManagerNative.getDefault().restartPackage(packageName); + } catch (RemoteException e) { + } + } + + /** + * Get the device configuration attributes. + */ + public ConfigurationInfo getDeviceConfigurationInfo() { + try { + return ActivityManagerNative.getDefault().getDeviceConfigurationInfo(); + } catch (RemoteException e) { + } + return null; + } + } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index ae9f3bf..53e6f34 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.content.ContentResolver; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; import android.content.res.Configuration; import android.graphics.Bitmap; @@ -83,6 +84,17 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM } /** + * Convenience for checking whether the system is ready. For internal use only. + */ + static public boolean isSystemReady() { + if (!sSystemReady) { + sSystemReady = getDefault().testIsSystemReady(); + } + return sSystemReady; + } + static boolean sSystemReady = false; + + /** * Convenience for sending a sticky broadcast. For internal use only. * If you don't care about permission, use null. */ @@ -959,6 +971,34 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case GET_DEVICE_CONFIGURATION_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + ConfigurationInfo config = getDeviceConfigurationInfo(); + reply.writeNoException(); + config.writeToParcel(reply, 0); + return true; + } + + case PROFILE_CONTROL_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + String process = data.readString(); + boolean start = data.readInt() != 0; + String path = data.readString(); + boolean res = profileControl(process, start, path); + reply.writeNoException(); + reply.writeInt(res ? 1 : 0); + return true; + } + + case PEEK_SERVICE_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + Intent service = Intent.CREATOR.createFromParcel(data); + String resolvedType = data.readString(); + IBinder binder = peekService(service, resolvedType); + reply.writeNoException(); + reply.writeStrongBinder(binder); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -1604,6 +1644,20 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } + + public IBinder peekService(Intent service, String resolvedType) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + service.writeToParcel(data, 0); + data.writeString(resolvedType); + mRemote.transact(PEEK_SERVICE_TRANSACTION, data, reply, 0); + reply.readException(); + IBinder binder = reply.readStrongBinder(); + reply.recycle(); + data.recycle(); + return binder; + } public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher) @@ -2028,6 +2082,11 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } + public boolean testIsSystemReady() + { + /* this base class version is never called */ + return true; + } public int handleApplicationError(IBinder app, int flags, String tag, String shortMsg, String longMsg, byte[] crashData) throws RemoteException @@ -2071,5 +2130,35 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + public ConfigurationInfo getDeviceConfigurationInfo() throws RemoteException + { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(GET_DEVICE_CONFIGURATION_TRANSACTION, data, reply, 0); + reply.readException(); + ConfigurationInfo res = ConfigurationInfo.CREATOR.createFromParcel(reply); + reply.recycle(); + data.recycle(); + return res; + } + + public boolean profileControl(String process, boolean start, + String path) throws RemoteException + { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeString(process); + data.writeInt(start ? 1 : 0); + data.writeString(path); + mRemote.transact(PROFILE_CONTROL_TRANSACTION, data, reply, 0); + reply.readException(); + boolean res = reply.readInt() != 0; + reply.recycle(); + data.recycle(); + return res; + } + private IBinder mRemote; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index e4c1057..f49005e 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -144,13 +144,19 @@ public final class ActivityThread { return sPackageManager; } - DisplayMetrics getDisplayMetricsLocked() { + DisplayMetrics getDisplayMetricsLocked(boolean forceUpdate) { + if (mDisplayMetrics != null && !forceUpdate) { + return mDisplayMetrics; + } if (mDisplay == null) { WindowManager wm = WindowManagerImpl.getDefault(); mDisplay = wm.getDefaultDisplay(); } - DisplayMetrics metrics = new DisplayMetrics(); + DisplayMetrics metrics = mDisplayMetrics = new DisplayMetrics(); mDisplay.getMetrics(metrics); + //Log.i("foo", "New metrics: w=" + metrics.widthPixels + " h=" + // + metrics.heightPixels + " den=" + metrics.density + // + " xdpi=" + metrics.xdpi + " ydpi=" + metrics.ydpi); return metrics; } @@ -173,7 +179,7 @@ public final class ActivityThread { if (assets.addAssetPath(appDir) == 0) { return null; } - DisplayMetrics metrics = getDisplayMetricsLocked(); + DisplayMetrics metrics = getDisplayMetricsLocked(false); r = new Resources(assets, metrics, getConfiguration()); //Log.i(TAG, "Created app resources " + r + ": " + r.getConfiguration()); // XXX need to remove entries when weak references go away @@ -235,7 +241,7 @@ public final class ActivityThread { ApplicationContext.createSystemContext(mainThread); mSystemContext.getResources().updateConfiguration( mainThread.getConfiguration(), - mainThread.getDisplayMetricsLocked()); + mainThread.getDisplayMetricsLocked(false)); //Log.i(TAG, "Created system resources " // + mSystemContext.getResources() + ": " // + mSystemContext.getResources().getConfiguration()); @@ -1205,7 +1211,10 @@ public final class ActivityThread { private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s"; private static final String ONE_COUNT_COLUMN = "%17s %8d"; private static final String TWO_COUNT_COLUMNS = "%17s %8d %17s %8d"; - + + // Formatting for checkin service - update version if row format changes + private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 1; + public final void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges) { queueOrSendMessage( @@ -1440,6 +1449,10 @@ public final class ActivityThread { } } + public void profilerControl(boolean start, String path) { + queueOrSendMessage(H.PROFILER_CONTROL, path, start ? 1 : 0); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { long nativeMax = Debug.getNativeHeapSize() / 1024; @@ -1462,7 +1475,101 @@ public final class ActivityThread { long dalvikMax = runtime.totalMemory() / 1024; long dalvikFree = runtime.freeMemory() / 1024; long dalvikAllocated = dalvikMax - dalvikFree; - + long viewInstanceCount = ViewDebug.getViewInstanceCount(); + long viewRootInstanceCount = ViewDebug.getViewRootInstanceCount(); + long appContextInstanceCount = ApplicationContext.getInstanceCount(); + long activityInstanceCount = Activity.getInstanceCount(); + int globalAssetCount = AssetManager.getGlobalAssetCount(); + int globalAssetManagerCount = AssetManager.getGlobalAssetManagerCount(); + int binderLocalObjectCount = Debug.getBinderLocalObjectCount(); + int binderProxyObjectCount = Debug.getBinderProxyObjectCount(); + int binderDeathObjectCount = Debug.getBinderDeathObjectCount(); + int openSslSocketCount = OpenSSLSocketImpl.getInstanceCount(); + long sqliteAllocated = SQLiteDebug.getHeapAllocatedSize() / 1024; + SQLiteDebug.PagerStats stats = new SQLiteDebug.PagerStats(); + SQLiteDebug.getPagerStats(stats); + + // Check to see if we were called by checkin server. If so, print terse format. + boolean doCheckinFormat = false; + if (args != null) { + for (String arg : args) { + if ("-c".equals(arg)) doCheckinFormat = true; + } + } + + // For checkin, we print one long comma-separated list of values + if (doCheckinFormat) { + // NOTE: if you change anything significant below, also consider changing + // ACTIVITY_THREAD_CHECKIN_VERSION. + String processName = (mBoundApplication != null) + ? mBoundApplication.processName : "unknown"; + + // Header + pw.print(ACTIVITY_THREAD_CHECKIN_VERSION); pw.print(','); + pw.print(Process.myPid()); pw.print(','); + pw.print(processName); pw.print(','); + + // Heap info - max + pw.print(nativeMax); pw.print(','); + pw.print(dalvikMax); pw.print(','); + pw.print("N/A,"); + pw.print(nativeMax + dalvikMax); pw.print(','); + + // Heap info - allocated + pw.print(nativeAllocated); pw.print(','); + pw.print(dalvikAllocated); pw.print(','); + pw.print("N/A,"); + pw.print(nativeAllocated + dalvikAllocated); pw.print(','); + + // Heap info - free + pw.print(nativeFree); pw.print(','); + pw.print(dalvikFree); pw.print(','); + pw.print("N/A,"); + pw.print(nativeFree + dalvikFree); pw.print(','); + + // Heap info - proportional set size + pw.print(memInfo.nativePss); pw.print(','); + pw.print(memInfo.dalvikPss); pw.print(','); + pw.print(memInfo.otherPss); pw.print(','); + pw.print(memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss); pw.print(','); + + // Heap info - shared + pw.print(nativeShared); pw.print(','); + pw.print(dalvikShared); pw.print(','); + pw.print(otherShared); pw.print(','); + pw.print(nativeShared + dalvikShared + otherShared); pw.print(','); + + // Heap info - private + pw.print(nativePrivate); pw.print(','); + pw.print(dalvikPrivate); pw.print(','); + pw.print(otherPrivate); pw.print(','); + pw.print(nativePrivate + dalvikPrivate + otherPrivate); pw.print(','); + + // Object counts + pw.print(viewInstanceCount); pw.print(','); + pw.print(viewRootInstanceCount); pw.print(','); + pw.print(appContextInstanceCount); pw.print(','); + pw.print(activityInstanceCount); pw.print(','); + + pw.print(globalAssetCount); pw.print(','); + pw.print(globalAssetManagerCount); pw.print(','); + pw.print(binderLocalObjectCount); pw.print(','); + pw.print(binderProxyObjectCount); pw.print(','); + + pw.print(binderDeathObjectCount); pw.print(','); + pw.print(openSslSocketCount); pw.print(','); + + // SQL + pw.print(sqliteAllocated); pw.print(','); + pw.print(stats.databaseBytes / 1024); pw.print(','); + pw.print(stats.numPagers); pw.print(','); + pw.print((stats.totalBytes - stats.referencedBytes) / 1024); pw.print(','); + pw.print(stats.referencedBytes / 1024); pw.print('\n'); + + return; + } + + // otherwise, show human-readable format printRow(pw, HEAP_COLUMN, "", "native", "dalvik", "other", "total"); printRow(pw, HEAP_COLUMN, "size:", nativeMax, dalvikMax, "N/A", nativeMax + dalvikMax); printRow(pw, HEAP_COLUMN, "allocated:", nativeAllocated, dalvikAllocated, "N/A", @@ -1480,26 +1587,22 @@ public final class ActivityThread { pw.println(" "); pw.println(" Objects"); - printRow(pw, TWO_COUNT_COLUMNS, "Views:", ViewDebug.getViewInstanceCount(), "ViewRoots:", - ViewDebug.getViewRootInstanceCount()); + printRow(pw, TWO_COUNT_COLUMNS, "Views:", viewInstanceCount, "ViewRoots:", + viewRootInstanceCount); - printRow(pw, TWO_COUNT_COLUMNS, "AppContexts:", ApplicationContext.getInstanceCount(), - "Activities:", Activity.getInstanceCount()); + printRow(pw, TWO_COUNT_COLUMNS, "AppContexts:", appContextInstanceCount, + "Activities:", activityInstanceCount); - printRow(pw, TWO_COUNT_COLUMNS, "Assets:", AssetManager.getGlobalAssetCount(), - "AssetManagers:", AssetManager.getGlobalAssetManagerCount()); + printRow(pw, TWO_COUNT_COLUMNS, "Assets:", globalAssetCount, + "AssetManagers:", globalAssetManagerCount); - printRow(pw, TWO_COUNT_COLUMNS, "Local Binders:", Debug.getBinderLocalObjectCount(), - "Proxy Binders:", Debug.getBinderProxyObjectCount()); - printRow(pw, ONE_COUNT_COLUMN, "Death Recipients:", Debug.getBinderDeathObjectCount()); - - printRow(pw, ONE_COUNT_COLUMN, "OpenSSL Sockets:", OpenSSLSocketImpl.getInstanceCount()); + printRow(pw, TWO_COUNT_COLUMNS, "Local Binders:", binderLocalObjectCount, + "Proxy Binders:", binderProxyObjectCount); + printRow(pw, ONE_COUNT_COLUMN, "Death Recipients:", binderDeathObjectCount); + printRow(pw, ONE_COUNT_COLUMN, "OpenSSL Sockets:", openSslSocketCount); + // SQLite mem info - long sqliteAllocated = SQLiteDebug.getHeapAllocatedSize() / 1024; - SQLiteDebug.PagerStats stats = new SQLiteDebug.PagerStats(); - SQLiteDebug.getPagerStats(stats); - pw.println(" "); pw.println(" SQL"); printRow(pw, TWO_COUNT_COLUMNS, "heap:", sqliteAllocated, "dbFiles:", @@ -1542,6 +1645,7 @@ public final class ActivityThread { public static final int LOW_MEMORY = 124; public static final int ACTIVITY_CONFIGURATION_CHANGED = 125; public static final int RELAUNCH_ACTIVITY = 126; + public static final int PROFILER_CONTROL = 127; String codeToString(int code) { if (localLOGV) { switch (code) { @@ -1572,6 +1676,7 @@ public final class ActivityThread { case LOW_MEMORY: return "LOW_MEMORY"; case ACTIVITY_CONFIGURATION_CHANGED: return "ACTIVITY_CONFIGURATION_CHANGED"; case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY"; + case PROFILER_CONTROL: return "PROFILER_CONTROL"; } } return "(unknown)"; @@ -1671,6 +1776,9 @@ public final class ActivityThread { case ACTIVITY_CONFIGURATION_CHANGED: handleActivityConfigurationChanged((IBinder)msg.obj); break; + case PROFILER_CONTROL: + handleProfilerControl(msg.arg1 != 0, (String)msg.obj); + break; } } } @@ -1751,6 +1859,7 @@ public final class ActivityThread { final HashMap<String, WeakReference<PackageInfo>> mResourcePackages = new HashMap<String, WeakReference<PackageInfo>>(); Display mDisplay = null; + DisplayMetrics mDisplayMetrics = null; HashMap<String, WeakReference<Resources> > mActiveResources = new HashMap<String, WeakReference<Resources> >(); @@ -1918,7 +2027,7 @@ public final class ActivityThread { PackageInfo info = new PackageInfo(this, "android", context); context.init(info, null, this); context.getResources().updateConfiguration( - getConfiguration(), getDisplayMetricsLocked()); + getConfiguration(), getDisplayMetricsLocked(false)); mSystemContext = context; //Log.i(TAG, "Created system resources " + context.getResources() // + ": " + context.getResources().getConfiguration()); @@ -2557,7 +2666,10 @@ public final class ActivityThread { a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; - wm.addView(decor, l); + if (a.mVisibleFromClient) { + a.mWindowAdded = true; + wm.addView(decor, l); + } // If the window has already been added, but during resume // we started another activity, then don't yet make the @@ -2576,7 +2688,8 @@ public final class ActivityThread { performConfigurationChanged(r.activity, r.newConfig); r.newConfig = null; } - Log.v(TAG, "Resuming " + r + " with isForward=" + isForward); + if (localLOGV) Log.v(TAG, "Resuming " + r + " with isForward=" + + isForward); WindowManager.LayoutParams l = r.window.getAttributes(); if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) @@ -2588,8 +2701,11 @@ public final class ActivityThread { View decor = r.window.getDecorView(); wm.updateViewLayout(decor, l); } - r.activity.mDecor.setVisibility(View.VISIBLE); + r.activity.mVisibleFromServer = true; mNumVisibleActivities++; + if (r.activity.mVisibleFromClient) { + r.activity.makeVisible(); + } } r.nextIdle = mNewActivities; @@ -2800,18 +2916,22 @@ public final class ActivityThread { View v = r.activity.mDecor; if (v != null) { if (show) { - if (v.getVisibility() != View.VISIBLE) { - v.setVisibility(View.VISIBLE); + if (!r.activity.mVisibleFromServer) { + r.activity.mVisibleFromServer = true; mNumVisibleActivities++; + if (r.activity.mVisibleFromClient) { + r.activity.makeVisible(); + } } if (r.newConfig != null) { performConfigurationChanged(r.activity, r.newConfig); r.newConfig = null; } } else { - if (v.getVisibility() == View.VISIBLE) { - v.setVisibility(View.INVISIBLE); + if (r.activity.mVisibleFromServer) { + r.activity.mVisibleFromServer = false; mNumVisibleActivities--; + v.setVisibility(View.INVISIBLE); } } } @@ -3037,11 +3157,13 @@ public final class ActivityThread { WindowManager wm = r.activity.getWindowManager(); View v = r.activity.mDecor; if (v != null) { - if (v.getVisibility() == View.VISIBLE) { + if (r.activity.mVisibleFromServer) { mNumVisibleActivities--; } IBinder wtoken = v.getWindowToken(); - wm.removeViewImmediate(v); + if (r.activity.mWindowAdded) { + wm.removeViewImmediate(v); + } if (wtoken != null) { WindowManagerImpl.getDefault().closeAll(wtoken, r.activity.getClass().getName(), "Activity"); @@ -3271,6 +3393,7 @@ public final class ActivityThread { mConfiguration = new Configuration(); } mConfiguration.updateFrom(config); + DisplayMetrics dm = getDisplayMetricsLocked(true); // set it for java, this also affects newly created Resources if (config.locale != null) { @@ -3290,7 +3413,7 @@ public final class ActivityThread { WeakReference<Resources> v = it.next(); Resources r = v.get(); if (r != null) { - r.updateConfiguration(config, null); + r.updateConfiguration(config, dm); //Log.i(TAG, "Updated app resources " + v.getKey() // + " " + r + ": " + r.getConfiguration()); } else { @@ -3318,6 +3441,21 @@ public final class ActivityThread { performConfigurationChanged(r.activity, mConfiguration); } + final void handleProfilerControl(boolean start, String path) { + if (start) { + File file = new File(path); + file.getParentFile().mkdirs(); + try { + Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024); + } catch (RuntimeException e) { + Log.w(TAG, "Profiling failed on path " + path + + " -- can the process access this path?"); + } + } else { + Debug.stopMethodTracing(); + } + } + final void handleLowMemory() { ArrayList<ComponentCallbacks> callbacks = new ArrayList<ComponentCallbacks>(); diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java index 4236a00..3b5ad86 100644 --- a/core/java/android/app/ApplicationContext.java +++ b/core/java/android/app/ApplicationContext.java @@ -1487,7 +1487,7 @@ class ApplicationContext extends Context { static final class ApplicationPackageManager extends PackageManager { @Override public PackageInfo getPackageInfo(String packageName, int flags) - throws NameNotFoundException { + throws NameNotFoundException { try { PackageInfo pi = mPM.getPackageInfo(packageName, flags); if (pi != null) { @@ -1500,6 +1500,43 @@ class ApplicationContext extends Context { throw new NameNotFoundException(packageName); } + public Intent getLaunchIntentForPackage(String packageName) + throws NameNotFoundException { + // First see if the package has an INFO activity; the existence of + // such an activity is implied to be the desired front-door for the + // overall package (such as if it has multiple launcher entries). + Intent intent = getLaunchIntentForPackageCategory(this, packageName, + Intent.CATEGORY_INFO); + if (intent != null) { + return intent; + } + + // Otherwise, try to find a main launcher activity. + return getLaunchIntentForPackageCategory(this, packageName, + Intent.CATEGORY_LAUNCHER); + } + + // XXX This should be implemented as a call to the package manager, + // to reduce the work needed. + static Intent getLaunchIntentForPackageCategory(PackageManager pm, + String packageName, String category) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + Intent intentToResolve = new Intent(Intent.ACTION_MAIN, null); + intentToResolve.addCategory(category); + final List<ResolveInfo> apps = + pm.queryIntentActivities(intentToResolve, 0); + // I wish there were a way to directly get the "main" activity of a + // package but ... + for (ResolveInfo app : apps) { + if (app.activityInfo.packageName.equals(packageName)) { + intent.setClassName(packageName, app.activityInfo.name); + return intent; + } + } + return null; + } + @Override public int[] getPackageGids(String packageName) throws NameNotFoundException { @@ -1630,6 +1667,15 @@ class ApplicationContext extends Context { } @Override + public String[] getSystemSharedLibraryNames() { + try { + return mPM.getSystemSharedLibraryNames(); + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + } + + @Override public int checkPermission(String permName, String pkgName) { try { return mPM.checkPermission(permName, pkgName); @@ -1974,6 +2020,18 @@ class ApplicationContext extends Context { getApplicationInfo(appPackageName, 0)); } + int mCachedSafeMode = -1; + @Override public boolean isSafeMode() { + try { + if (mCachedSafeMode < 0) { + mCachedSafeMode = mPM.isSafeMode() ? 1 : 0; + } + return mCachedSafeMode != 0; + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + } + static void configurationChanged() { synchronized (sSync) { sIconCache.clear(); diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index d2cf55a..bcc9302 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -322,6 +322,15 @@ public abstract class ApplicationThreadNative extends Binder requestPss(); return true; } + + case PROFILER_CONTROL_TRANSACTION: + { + data.enforceInterface(IApplicationThread.descriptor); + boolean start = data.readInt() != 0; + String path = data.readString(); + profilerControl(start, path); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -654,5 +663,14 @@ class ApplicationThreadProxy implements IApplicationThread { data.recycle(); } + public void profilerControl(boolean start, String path) throws RemoteException { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken(IApplicationThread.descriptor); + data.writeInt(start ? 1 : 0); + data.writeString(path); + mRemote.transact(PROFILER_CONTROL_TRANSACTION, data, null, + IBinder.FLAG_ONEWAY); + data.recycle(); + } } diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 951b48d..b09a57f 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -122,7 +122,7 @@ public class Dialog implements DialogInterface, Window.Callback, * uses the window manager and theme from this context to * present its UI. * @param theme A style resource describing the theme to use for the - * window. See <a href="{@docRoot}reference/available-resources.html#stylesandthemes">Style + * window. See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">Style * and Theme Resources</a> for more information about defining and using * styles. This theme is applied on top of the current theme in * <var>context</var>. If 0, the default dialog theme will be used. @@ -518,7 +518,7 @@ public class Dialog implements DialogInterface, Window.Callback, private boolean isOutOfBounds(MotionEvent event) { final int x = (int) event.getX(); final int y = (int) event.getY(); - final int slop = ViewConfiguration.getWindowTouchSlop(); + final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop(); final View decorView = getWindow().getDecorView(); return (x < -slop) || (y < -slop) || (x > (decorView.getWidth()+slop)) diff --git a/core/java/android/app/ExpandableListActivity.java b/core/java/android/app/ExpandableListActivity.java index 75dfcae..a2e048f 100644 --- a/core/java/android/app/ExpandableListActivity.java +++ b/core/java/android/app/ExpandableListActivity.java @@ -63,21 +63,21 @@ import java.util.Map; * * <pre> * <?xml version="1.0" encoding="UTF-8"?> - * <LinearLayout + * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" * android:orientation="vertical" * android:layout_width="fill_parent" * android:layout_height="fill_parent" - * android:paddingLeft="8" - * android:paddingRight="8"> + * android:paddingLeft="8dp" + * android:paddingRight="8dp"> * - * <ExpandableListView id="android:list" + * <ExpandableListView android:id="@id/android:list" * android:layout_width="fill_parent" * android:layout_height="fill_parent" * android:background="#00FF00" * android:layout_weight="1" * android:drawSelectorOnTop="false"/> * - * <TextView id="android:empty" + * <TextView android:id="@id/android:empty" * android:layout_width="fill_parent" * android:layout_height="fill_parent" * android:background="#FF0000" @@ -113,19 +113,19 @@ import java.util.Map; * * <pre> * <?xml version="1.0" encoding="utf-8"?> - * <LinearLayout + * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" * android:layout_width="fill_parent" * android:layout_height="wrap_content" * android:orientation="vertical"> * - * <TextView id="text1" - * android:textSize="16" + * <TextView android:id="@+id/text1" + * android:textSize="16sp" * android:textStyle="bold" * android:layout_width="fill_parent" * android:layout_height="wrap_content"/> * - * <TextView id="text2" - * android:textSize="16" + * <TextView android:id="@+id/text2" + * android:textSize="16sp" * android:layout_width="fill_parent" * android:layout_height="wrap_content"/> * </LinearLayout> @@ -162,7 +162,8 @@ public class ExpandableListActivity extends Activity implements /** * Override this to populate the context menu when an item is long pressed. menuInfo - * will contain a {@link AdapterContextMenuInfo} whose position is a packed position + * will contain an {@link android.widget.ExpandableListView.ExpandableListContextMenuInfo} + * whose packedPosition is a packed position * that should be used with {@link ExpandableListView#getPackedPositionType(long)} and * the other similar methods. * <p> diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 353500e..2ac6160 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -22,6 +22,7 @@ import android.content.ContentProviderNative; import android.content.IContentProvider; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.ProviderInfo; import android.content.res.Configuration; @@ -127,7 +128,8 @@ public interface IActivityManager extends IInterface { boolean doRebind) throws RemoteException; /* oneway */ public void serviceDoneExecuting(IBinder token) throws RemoteException; - + public IBinder peekService(Intent service, String resolvedType) throws RemoteException; + public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher) throws RemoteException; @@ -216,6 +218,17 @@ public interface IActivityManager extends IInterface { // Retrieve running application processes in the system public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() throws RemoteException; + // Get device configuration + public ConfigurationInfo getDeviceConfigurationInfo() throws RemoteException; + + // Turn on/off profiling in a particular process. + public boolean profileControl(String process, boolean start, + String path) throws RemoteException; + + /* + * Private non-Binder interfaces + */ + /* package */ boolean testIsSystemReady(); /** Information you can retrieve about a particular application. */ public static class ContentProviderHolder implements Parcelable { @@ -354,4 +367,7 @@ public interface IActivityManager extends IInterface { int GET_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+80; int REPORT_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+81; int GET_RUNNING_APP_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+82; + int GET_DEVICE_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+83; + int PEEK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+84; + int PROFILE_CONTROL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+85; } diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index 47476b5..9f3534b 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -86,6 +86,7 @@ public interface IApplicationThread extends IInterface { void scheduleLowMemory() throws RemoteException; void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException; void requestPss() throws RemoteException; + void profilerControl(boolean start, String path) throws RemoteException; String descriptor = "android.app.IApplicationThread"; @@ -115,4 +116,5 @@ public interface IApplicationThread extends IInterface { int SCHEDULE_ACTIVITY_CONFIGURATION_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+24; int SCHEDULE_RELAUNCH_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+25; int REQUEST_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+26; + int PROFILER_CONTROL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27; } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index f96d787..f6a28b2 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1267,7 +1267,7 @@ public class Instrumentation { } /** - * Perform calling of an activity's {@link Activity#onUserLeaving} method. + * Perform calling of an activity's {@link Activity#onUserLeaveHint} method. * The default implementation simply calls through to that method. * * @param activity The activity being notified that the user has navigated away diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java new file mode 100644 index 0000000..2b12a2a --- /dev/null +++ b/core/java/android/app/IntentService.java @@ -0,0 +1,74 @@ +package android.app; + +import android.content.Intent; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; + +/** + * An abstract {@link Service} that serializes the handling of the Intents passed upon service + * start and handles them on a handler thread. + * + * <p>To use this class extend it and implement {@link #onHandleIntent}. The {@link Service} will + * automatically be stopped when the last enqueued {@link Intent} is handled. + */ +public abstract class IntentService extends Service { + private volatile Looper mServiceLooper; + private volatile ServiceHandler mServiceHandler; + private String mName; + + private final class ServiceHandler extends Handler { + public ServiceHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + onHandleIntent((Intent)msg.obj); + stopSelf(msg.arg1); + } + } + + public IntentService(String name) { + super(); + mName = name; + } + + @Override + public void onCreate() { + super.onCreate(); + HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); + thread.start(); + + mServiceLooper = thread.getLooper(); + mServiceHandler = new ServiceHandler(mServiceLooper); + } + + @Override + public void onStart(Intent intent, int startId) { + super.onStart(intent, startId); + Message msg = mServiceHandler.obtainMessage(); + msg.arg1 = startId; + msg.obj = intent; + mServiceHandler.sendMessage(msg); + } + + @Override + public void onDestroy() { + mServiceLooper.quit(); + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + /** + * Invoked on the Handler thread with the {@link Intent} that is passed to {@link #onStart}. + * Note that this will be invoked from a different thread than the one that handles the + * {@link #onStart} call. + */ + protected abstract void onHandleIntent(Intent intent); +} diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java index 8f0a4f5..d6fcbb1 100644 --- a/core/java/android/app/LauncherActivity.java +++ b/core/java/android/app/LauncherActivity.java @@ -18,14 +18,23 @@ package android.app; import android.content.Context; import android.content.Intent; -import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PaintFlagsDrawFilter; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.PaintDrawable; import android.os.Bundle; -import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.Window; import android.widget.BaseAdapter; import android.widget.Filter; import android.widget.Filterable; @@ -43,33 +52,59 @@ import java.util.List; * */ public abstract class LauncherActivity extends ListActivity { + + Intent mIntent; + PackageManager mPackageManager; /** + * An item in the list + */ + public static class ListItem { + public CharSequence label; + //public CharSequence description; + public Drawable icon; + public String packageName; + public String className; + + ListItem(PackageManager pm, ResolveInfo resolveInfo, IconResizer resizer) { + label = resolveInfo.loadLabel(pm); + if (label == null && resolveInfo.activityInfo != null) { + label = resolveInfo.activityInfo.name; + } + + /* + if (resolveInfo.activityInfo != null && + resolveInfo.activityInfo.applicationInfo != null) { + description = resolveInfo.activityInfo.applicationInfo.loadDescription(pm); + } + */ + + icon = resizer.createIconThumbnail(resolveInfo.loadIcon(pm)); + packageName = resolveInfo.activityInfo.applicationInfo.packageName; + className = resolveInfo.activityInfo.name; + } + + public ListItem() { + } + } + + /** * Adapter which shows the set of activities that can be performed for a given intent. */ private class ActivityAdapter extends BaseAdapter implements Filterable { private final Object lock = new Object(); - private ArrayList<ResolveInfo> mOriginalValues; + private ArrayList<ListItem> mOriginalValues; - protected final Context mContext; - protected final Intent mIntent; protected final LayoutInflater mInflater; - protected List<ResolveInfo> mActivitiesList; + protected List<ListItem> mActivitiesList; private Filter mFilter; - - public ActivityAdapter(Context context, Intent intent) { - mContext = context; - mIntent = new Intent(intent); - mIntent.setComponent(null); - mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - PackageManager pm = context.getPackageManager(); - mActivitiesList = pm.queryIntentActivities(intent, 0); - if (mActivitiesList != null) { - Collections.sort(mActivitiesList, new ResolveInfo.DisplayNameComparator(pm)); - } + + public ActivityAdapter() { + mInflater = (LayoutInflater) LauncherActivity.this.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + mActivitiesList = makeListItems(); } public Intent intentForPosition(int position) { @@ -78,8 +113,8 @@ public abstract class LauncherActivity extends ListActivity { } Intent intent = new Intent(mIntent); - ActivityInfo ai = mActivitiesList.get(position).activityInfo; - intent.setClassName(ai.applicationInfo.packageName, ai.name); + ListItem item = mActivitiesList.get(position); + intent.setClassName(item.packageName, item.className); return intent; } @@ -99,7 +134,7 @@ public abstract class LauncherActivity extends ListActivity { View view; if (convertView == null) { view = mInflater.inflate( - com.android.internal.R.layout.simple_list_item_1, parent, false); + com.android.internal.R.layout.activity_list_item_2, parent, false); } else { view = convertView; } @@ -107,35 +142,22 @@ public abstract class LauncherActivity extends ListActivity { return view; } - private char getCandidateLetter(ResolveInfo info) { - PackageManager pm = mContext.getPackageManager(); - CharSequence label = info.loadLabel(pm); - - if (label == null) { - label = info.activityInfo.name; - } - - return Character.toLowerCase(label.charAt(0)); + private void bindView(View view, ListItem item) { + TextView text = (TextView) view; + text.setText(item.label); + text.setCompoundDrawablesWithIntrinsicBounds(item.icon, null, null, null); } - - private void bindView(View view, ResolveInfo info) { - TextView text = (TextView) view.findViewById(com.android.internal.R.id.text1); - - PackageManager pm = mContext.getPackageManager(); - CharSequence label = info.loadLabel(pm); - text.setText(label != null ? label : info.activityInfo.name); - } - + public Filter getFilter() { if (mFilter == null) { mFilter = new ArrayFilter(); } return mFilter; } - + /** - * <p>An array filters constrains the content of the array adapter with a prefix. Each item that - * does not start with the supplied prefix is removed from the list.</p> + * An array filters constrains the content of the array adapter with a prefix. Each + * item that does not start with the supplied prefix is removed from the list. */ private class ArrayFilter extends Filter { @Override @@ -144,39 +166,35 @@ public abstract class LauncherActivity extends ListActivity { if (mOriginalValues == null) { synchronized (lock) { - mOriginalValues = new ArrayList<ResolveInfo>(mActivitiesList); + mOriginalValues = new ArrayList<ListItem>(mActivitiesList); } } if (prefix == null || prefix.length() == 0) { synchronized (lock) { - ArrayList<ResolveInfo> list = new ArrayList<ResolveInfo>(mOriginalValues); + ArrayList<ListItem> list = new ArrayList<ListItem>(mOriginalValues); results.values = list; results.count = list.size(); } } else { - final PackageManager pm = mContext.getPackageManager(); final String prefixString = prefix.toString().toLowerCase(); - ArrayList<ResolveInfo> values = mOriginalValues; + ArrayList<ListItem> values = mOriginalValues; int count = values.size(); - ArrayList<ResolveInfo> newValues = new ArrayList<ResolveInfo>(count); + ArrayList<ListItem> newValues = new ArrayList<ListItem>(count); for (int i = 0; i < count; i++) { - ResolveInfo value = values.get(i); + ListItem item = values.get(i); - final CharSequence label = value.loadLabel(pm); - final CharSequence name = label != null ? label : value.activityInfo.name; - - String[] words = name.toString().toLowerCase().split(" "); + String[] words = item.label.toString().toLowerCase().split(" "); int wordCount = words.length; for (int k = 0; k < wordCount; k++) { final String word = words[k]; if (word.startsWith(prefixString)) { - newValues.add(value); + newValues.add(item); break; } } @@ -192,7 +210,7 @@ public abstract class LauncherActivity extends ListActivity { @Override protected void publishResults(CharSequence constraint, FilterResults results) { //noinspection unchecked - mActivitiesList = (List<ResolveInfo>) results.values; + mActivitiesList = (List<ListItem>) results.values; if (results.count > 0) { notifyDataSetChanged(); } else { @@ -201,19 +219,119 @@ public abstract class LauncherActivity extends ListActivity { } } } - - + + /** + * Utility class to resize icons to match default icon size. + */ + public class IconResizer { + // Code is borrowed from com.android.launcher.Utilities. + private int mIconWidth = -1; + private int mIconHeight = -1; + + private final Rect mOldBounds = new Rect(); + private Canvas mCanvas = new Canvas(); + + public IconResizer() { + mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, + Paint.FILTER_BITMAP_FLAG)); + + final Resources resources = LauncherActivity.this.getResources(); + mIconWidth = mIconHeight = (int) resources.getDimension( + android.R.dimen.app_icon_size); + } + + /** + * Returns a Drawable representing the thumbnail of the specified Drawable. + * The size of the thumbnail is defined by the dimension + * android.R.dimen.launcher_application_icon_size. + * + * This method is not thread-safe and should be invoked on the UI thread only. + * + * @param icon The icon to get a thumbnail of. + * + * @return A thumbnail for the specified icon or the icon itself if the + * thumbnail could not be created. + */ + public Drawable createIconThumbnail(Drawable icon) { + int width = mIconWidth; + int height = mIconHeight; + + final int iconWidth = icon.getIntrinsicWidth(); + final int iconHeight = icon.getIntrinsicHeight(); + + if (icon instanceof PaintDrawable) { + PaintDrawable painter = (PaintDrawable) icon; + painter.setIntrinsicWidth(width); + painter.setIntrinsicHeight(height); + } + + if (width > 0 && height > 0) { + if (width < iconWidth || height < iconHeight) { + final float ratio = (float) iconWidth / iconHeight; + + if (iconWidth > iconHeight) { + height = (int) (width / ratio); + } else if (iconHeight > iconWidth) { + width = (int) (height * ratio); + } + + final Bitmap.Config c = icon.getOpacity() != PixelFormat.OPAQUE ? + Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; + final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c); + final Canvas canvas = mCanvas; + canvas.setBitmap(thumb); + // Copy the old bounds to restore them later + // If we were to do oldBounds = icon.getBounds(), + // the call to setBounds() that follows would + // change the same instance and we would lose the + // old bounds + mOldBounds.set(icon.getBounds()); + final int x = (mIconWidth - width) / 2; + final int y = (mIconHeight - height) / 2; + icon.setBounds(x, y, x + width, y + height); + icon.draw(canvas); + icon.setBounds(mOldBounds); + icon = new BitmapDrawable(thumb); + } else if (iconWidth < width && iconHeight < height) { + final Bitmap.Config c = Bitmap.Config.ARGB_8888; + final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c); + final Canvas canvas = mCanvas; + canvas.setBitmap(thumb); + mOldBounds.set(icon.getBounds()); + final int x = (width - iconWidth) / 2; + final int y = (height - iconHeight) / 2; + icon.setBounds(x, y, x + iconWidth, y + iconHeight); + icon.draw(canvas); + icon.setBounds(mOldBounds); + icon = new BitmapDrawable(thumb); + } + } + + return icon; + } + } + @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + mPackageManager = getPackageManager(); + + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setProgressBarIndeterminateVisibility(true); + setContentView(com.android.internal.R.layout.activity_list); - mAdapter = new ActivityAdapter(this, getTargetIntent()); + + mIntent = new Intent(getTargetIntent()); + mIntent.setComponent(null); + mAdapter = new ActivityAdapter(); setListAdapter(mAdapter); getListView().setTextFilterEnabled(true); + + setProgressBarIndeterminateVisibility(false); } - @Override protected void onListItemClick(ListView l, View v, int position, long id) { Intent intent = ((ActivityAdapter)mAdapter).intentForPosition(position); @@ -221,6 +339,42 @@ public abstract class LauncherActivity extends ListActivity { startActivity(intent); } - protected abstract Intent getTargetIntent(); - + /** + * Return the actual Intent for a specific position in our + * {@link android.widget.ListView}. + * @param position The item whose Intent to return + */ + protected Intent intentForPosition(int position) { + ActivityAdapter adapter = (ActivityAdapter) mAdapter; + return adapter.intentForPosition(position); + } + + /** + * Get the base intent to use when running + * {@link PackageManager#queryIntentActivities(Intent, int)}. + */ + protected Intent getTargetIntent() { + return new Intent(); + } + + /** + * Perform the query to determine which results to show and return a list of them. + */ + public List<ListItem> makeListItems() { + // Load all matching activities and sort correctly + List<ResolveInfo> list = mPackageManager.queryIntentActivities(mIntent, + /* no flags */ 0); + Collections.sort(list, new ResolveInfo.DisplayNameComparator(mPackageManager)); + + IconResizer resizer = new IconResizer(); + + ArrayList<ListItem> result = new ArrayList<ListItem>(list.size()); + int listSize = list.size(); + for (int i = 0; i < listSize; i++) { + ResolveInfo resolveInfo = list.get(i); + result.add(new ListItem(mPackageManager, resolveInfo, resizer)); + } + + return result; + } } diff --git a/core/java/android/app/ListActivity.java b/core/java/android/app/ListActivity.java index 2818937..5523c18 100644 --- a/core/java/android/app/ListActivity.java +++ b/core/java/android/app/ListActivity.java @@ -53,22 +53,22 @@ import android.widget.ListView; * </p> * * <pre> - * <?xml version="1.0" encoding="UTF-8"?> - * <LinearLayout + * <?xml version="1.0" encoding="utf-8"?> + * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" * android:orientation="vertical" * android:layout_width="fill_parent" * android:layout_height="fill_parent" - * android:paddingLeft="8" - * android:paddingRight="8"> + * android:paddingLeft="8dp" + * android:paddingRight="8dp"> * - * <ListView id="android:list" + * <ListView android:id="@id/android:list" * android:layout_width="fill_parent" * android:layout_height="fill_parent" * android:background="#00FF00" * android:layout_weight="1" * android:drawSelectorOnTop="false"/> * - * <TextView id="android:empty" + * <TextView id="@id/android:empty" * android:layout_width="fill_parent" * android:layout_height="fill_parent" * android:background="#FF0000" @@ -99,19 +99,19 @@ import android.widget.ListView; * * <pre> * <?xml version="1.0" encoding="utf-8"?> - * <LinearLayout + * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" * android:layout_width="fill_parent" * android:layout_height="wrap_content" * android:orientation="vertical"> * - * <TextView id="text1" - * android:textSize="16" + * <TextView android:id="@+id/text1" + * android:textSize="16sp" * android:textStyle="bold" * android:layout_width="fill_parent" * android:layout_height="wrap_content"/> * - * <TextView id="text2" - * android:textSize="16" + * <TextView android:id="@+id/text2" + * android:textSize="16sp" * android:layout_width="fill_parent" * android:layout_height="wrap_content"/> * </LinearLayout> @@ -142,8 +142,8 @@ import android.widget.ListView; * public class MyListAdapter extends ListActivity { * * @Override - * protected void onCreate(Bundle icicle){ - * super.onCreate(icicle); + * protected void onCreate(Bundle savedInstanceState){ + * super.onCreate(savedInstanceState); * * // We'll define a custom screen layout here (the one shown above), but * // typically, you could just use the standard ListActivity layout. diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index ea67cdb..51fddb1 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -90,8 +90,9 @@ public class Notification implements Parcelable * The intent to execute when the expanded status entry is clicked. If * this is an activity, it must include the * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires - * that you take care of task management as described in the - * <a href="{@docRoot}intro/appmodel.html">application model</a> document. + * that you take care of task management as described in the <em>Activities and Tasks</em> + * section of the <a href="{@docRoot}guide/topics/fundamentals.html#acttask">Application + * Fundamentals</a> document. */ public PendingIntent contentIntent; @@ -420,8 +421,8 @@ public class Notification implements Parcelable * @param contentIntent The intent to launch when the user clicks the expanded notification. * If this is an activity, it must include the * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires - * that you take care of task management as described in the - * <a href="{@docRoot}intro/appmodel.html">application model</a> document. + * that you take care of task management as described in + * <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">Application Fundamentals: Activities and Tasks</a>. */ public void setLatestEventInfo(Context context, CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) { diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index afb3827..39edab7 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -82,9 +82,7 @@ public class NotificationManager * @param id An identifier for this notification unique within your * application. * @param notification A {@link Notification} object describing how to - * notify the user, other than the view you're providing. If you - * pass null, there will be no persistent notification and no - * flashing, vibration, etc. + * notify the user, other than the view you're providing. Must not be null. */ public void notify(int id, Notification notification) { diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index b59e9dc..1bed706 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -426,13 +426,9 @@ public final class PendingIntent implements Parcelable { */ @Override public boolean equals(Object otherObj) { - if (otherObj == null) { - return false; - } - try { + if (otherObj instanceof PendingIntent) { return mTarget.asBinder().equals(((PendingIntent)otherObj) .mTarget.asBinder()); - } catch (ClassCastException e) { } return false; } @@ -442,6 +438,13 @@ public final class PendingIntent implements Parcelable { return mTarget.asBinder().hashCode(); } + @Override + public String toString() { + return "PendingIntent{" + + Integer.toHexString(System.identityHashCode(this)) + + " target " + (mTarget != null ? mTarget.asBinder() : null) + "}"; + } + public int describeContents() { return 0; } diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 2e2a1a1..64288d2 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -16,11 +16,14 @@ package android.app; +import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; @@ -29,11 +32,11 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.server.search.SearchableInfo; +import android.speech.RecognizerIntent; import android.text.Editable; import android.text.InputType; import android.text.TextUtils; @@ -50,6 +53,7 @@ import android.widget.AdapterView; import android.widget.AutoCompleteTextView; import android.widget.Button; import android.widget.CursorAdapter; +import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ListView; import android.widget.SimpleCursorAdapter; @@ -94,6 +98,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private TextView mBadgeLabel; private AutoCompleteTextView mSearchTextField; private Button mGoButton; + private ImageButton mVoiceButton; // interaction with searchable application private ComponentName mLaunchComponent; @@ -115,10 +120,13 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private Uri mSuggestionData = null; private String mSuggestionQuery = null; + // For voice searching + private Intent mVoiceWebSearchIntent; + private Intent mVoiceAppSearchIntent; + // support for AutoCompleteTextView suggestions display private SuggestionsAdapter mSuggestionsAdapter; - /** * Constructor - fires it up and makes it look like the search UI. * @@ -153,12 +161,15 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mSearchTextField = (AutoCompleteTextView) findViewById(com.android.internal.R.id.search_src_text); mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn); + mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn); // attach listeners mSearchTextField.addTextChangedListener(mTextWatcher); mSearchTextField.setOnKeyListener(mTextKeyListener); mGoButton.setOnClickListener(mGoButtonClickListener); mGoButton.setOnKeyListener(mButtonsKeyListener); + mVoiceButton.setOnClickListener(mVoiceButtonClickListener); + mVoiceButton.setOnKeyListener(mButtonsKeyListener); // pre-hide all the extraneous elements mBadgeLabel.setVisibility(View.GONE); @@ -169,13 +180,19 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS setCanceledOnTouchOutside(true); // Set up broadcast filters - mCloseDialogsFilter = new - IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + mCloseDialogsFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); mPackageFilter = new IntentFilter(); mPackageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); mPackageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); mPackageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); mPackageFilter.addDataScheme("package"); + + // Save voice intent for later queries/launching + mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); + mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, + RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH); + + mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); } /** @@ -236,7 +253,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mSearchTextField.setAdapter(mSuggestionsAdapter); mSearchTextField.setText(initialQuery); } else { - mSuggestionsAdapter = new SuggestionsAdapter(getContext(), mSearchable); + mSuggestionsAdapter = new SuggestionsAdapter(getContext(), mSearchable, + mSearchTextField); mSearchTextField.setAdapter(mSuggestionsAdapter); // finally, load the user's initial text (which may trigger suggestions) @@ -261,16 +279,15 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** - * Dismiss the search dialog. + * The search dialog is being dismissed, so handle all of the local shutdown operations. * - * This function is designed to be idempotent so it can be safely called at any time + * This function is designed to be idempotent so that dismiss() can be safely called at any time * (even if already closed) and more likely to really dump any memory. No leaks! */ @Override - public void dismiss() { - if (isShowing()) { - super.dismiss(); - } + public void onStop() { + super.onStop(); + setOnCancelListener(null); setOnDismissListener(null); @@ -281,6 +298,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // This is OK - it just means we didn't have any registered } + // close any leftover cursor + if (mSuggestionsAdapter != null) { + mSuggestionsAdapter.changeCursor(null); + } + // dump extra memory we're hanging on to mLaunchComponent = null; mAppSearchData = null; @@ -408,6 +430,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS updateSearchButton(); updateSearchBadge(); updateQueryHint(); + updateVoiceButton(); // In order to properly configure the input method (if one is being used), we // need to let it know if we'll be providing suggestions. Although it would be @@ -426,6 +449,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } } mSearchTextField.setInputType(inputType); + mSearchTextField.setImeOptions(mSearchable.getImeOptions()); } } @@ -500,6 +524,30 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** + * Update the visibility of the voice button. There are actually two voice search modes, + * either of which will activate the button. + */ + private void updateVoiceButton() { + int visibility = View.GONE; + if (mSearchable.getVoiceSearchEnabled()) { + Intent testIntent = null; + if (mSearchable.getVoiceSearchLaunchWebSearch()) { + testIntent = mVoiceWebSearchIntent; + } else if (mSearchable.getVoiceSearchLaunchRecognizer()) { + testIntent = mVoiceAppSearchIntent; + } + if (testIntent != null) { + ResolveInfo ri = getContext().getPackageManager(). + resolveActivity(testIntent, PackageManager.MATCH_DEFAULT_ONLY); + if (ri != null) { + visibility = View.VISIBLE; + } + } + } + mVoiceButton.setVisibility(visibility); + } + + /** * Listeners of various types */ @@ -642,11 +690,97 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS }; /** + * React to a click in the voice search button. + */ + View.OnClickListener mVoiceButtonClickListener = new View.OnClickListener() { + public void onClick(View v) { + try { + if (mSearchable.getVoiceSearchLaunchWebSearch()) { + getContext().startActivity(mVoiceWebSearchIntent); + dismiss(); + } else if (mSearchable.getVoiceSearchLaunchRecognizer()) { + Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent); + getContext().startActivity(appSearchIntent); + dismiss(); + } + } catch (ActivityNotFoundException e) { + // Should not happen, since we check the availability of + // voice search before showing the button. But just in case... + Log.w(LOG_TAG, "Could not find voice search activity"); + } + } + }; + + /** + * Create and return an Intent that can launch the voice search activity, perform a specific + * voice transcription, and forward the results to the searchable activity. + * + * @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) { + // create the necessary intent to set up a search-and-forward operation + // in the voice search system. We have to keep the bundle separate, + // because it becomes immutable once it enters the PendingIntent + Intent queryIntent = new Intent(Intent.ACTION_SEARCH); + queryIntent.setComponent(mSearchable.mSearchActivity); + PendingIntent pending = PendingIntent.getActivity( + getContext(), 0, queryIntent, PendingIntent.FLAG_ONE_SHOT); + + // Now set up the bundle that will be inserted into the pending intent + // when it's time to do the search. We always build it here (even if empty) + // because the voice search activity will always need to insert "QUERY" into + // it anyway. + Bundle queryExtras = new Bundle(); + if (mAppSearchData != null) { + queryExtras.putBundle(SearchManager.APP_DATA, mAppSearchData); + } + + // Now build the intent to launch the voice search. Add all necessary + // extras to launch the voice recognizer, and then all the necessary extras + // to forward the results to the searchable activity + Intent voiceIntent = new Intent(baseIntent); + + // Add all of the configuration options supplied by the searchable's metadata + String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM; + String prompt = null; + String language = null; + int maxResults = 1; + Resources resources = mActivityContext.getResources(); + if (mSearchable.getVoiceLanguageModeId() != 0) { + languageModel = resources.getString(mSearchable.getVoiceLanguageModeId()); + } + if (mSearchable.getVoicePromptTextId() != 0) { + prompt = resources.getString(mSearchable.getVoicePromptTextId()); + } + if (mSearchable.getVoiceLanguageId() != 0) { + language = resources.getString(mSearchable.getVoiceLanguageId()); + } + if (mSearchable.getVoiceMaxResults() != 0) { + maxResults = mSearchable.getVoiceMaxResults(); + } + voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel); + voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt); + voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language); + voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults); + + // Add the values that configure forwarding the results + voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending); + voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras); + + return voiceIntent; + } + + /** * React to the user typing "enter" or other hardwired keys while typing in the search box. * This handles these special keys while the edit box has focus. */ View.OnKeyListener mTextKeyListener = new View.OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + cancel(); + return true; + } // also guard against possible race conditions (late arrival after dismiss) if (mSearchable != null && TextUtils.getTrimmedLength(mSearchTextField.getText()) > 0) { @@ -661,7 +795,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // otherwise, dispatch an "edit view" key switch (keyCode) { case KeyEvent.KEYCODE_ENTER: - case KeyEvent.KEYCODE_DPAD_CENTER: if (event.getAction() == KeyEvent.ACTION_UP) { v.cancelLongPress(); launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null); @@ -700,9 +833,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // also guard against possible race conditions (late arrival after dismiss) if (mSearchable != null) { handled = doSuggestionsKey(v, keyCode, event); - if (!handled) { - handled = refocusingKeyListener(v, keyCode, event); - } } return handled; } @@ -793,24 +923,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS }; /** - * UI-thread handling of dialog dismiss. Called by mBroadcastReceiver.onReceive(). - * - * TODO: This is a really heavyweight solution for something that should be so simple. - * For example, we already have a handler, in our superclass, why aren't we sharing that? - * I think we need to investigate simplifying this entire methodology, or perhaps boosting - * it up into the Dialog class. - */ - private static final int MESSAGE_DISMISS = 0; - private Handler mDismissHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - if (msg.what == MESSAGE_DISMISS) { - dismiss(); - } - } - }; - - /** * Various ways to launch searches */ @@ -907,6 +1019,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS * @param jamQuery True means to set the query, false means to reset it to the user's choice */ private void jamSuggestionQuery(boolean jamQuery, AdapterView<?> parent, int position) { + // quick check against race conditions + if (mSearchable == null) { + return; + } + mSuggestionsAdapter.setNonUserQuery(true); // disables any suggestions processing if (jamQuery) { CursorAdapter ca = getSuggestionsAdapter(parent); @@ -1180,10 +1297,13 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // These private variables are shared by the filter thread and must be protected private WeakReference<Cursor> mRecentCursor = new WeakReference<Cursor>(null); private boolean mNonUserQuery = false; + private AutoCompleteTextView mParentView; - public SuggestionsAdapter(Context context, SearchableInfo searchable) { + public SuggestionsAdapter(Context context, SearchableInfo searchable, + AutoCompleteTextView actv) { super(context, -1, null, null, null); mSearchable = searchable; + mParentView = actv; // set up provider resources (gives us icons, etc.) Context activityContext = mSearchable.getActivityContext(mContext); @@ -1296,9 +1416,12 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS to = ONE_LINE_TO; } } + // Force the underlying ListView to discard and reload all layouts + // (Note, this should be optimized for cases where layout/cursor remain same) + mParentView.resetListAndClearViews(); // Now actually set up the cursor, columns, and the list view changeCursorAndColumns(c, from, to); - setViewResource(layout); + setViewResource(layout); } else { // Provide some help for developers instead of just silently discarding Log.w(LOG_TAG, "Suggestions cursor discarded due to missing required columns."); diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 0a37e81..c1d66f4 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -168,7 +168,8 @@ import android.view.KeyEvent; * <p><b>Managing focus and knowing if Search is active.</b> The search UI is not a separate * activity, and when the UI is invoked or dismissed, your activity will not typically be paused, * resumed, or otherwise notified by the methods defined in - * <a href="android.app.Activity#ActivityLifecycle">Activity Lifecycle</a>. The search UI is + * <a href="{@docRoot}guide/topics/fundamentals.html#actlife">Application Fundamentals: + * Activity Lifecycle</a>. The search UI is * handled in the same way as other system UI elements which may appear from time to time, such as * notifications, screen locks, or other system alerts: * <p>When the search UI appears, your activity will lose input focus. @@ -212,11 +213,11 @@ import android.view.KeyEvent; * {@link #QUERY getStringExtra(SearchManager.QUERY)}.</li> * <li>To identify and support your searchable activity, you'll need to * provide an XML file providing searchability configuration parameters, a reference to that - * in your searchable activity's <a href="../../../devel/bblocks-manifest.html">manifest</a> + * in your searchable activity's <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> * entry, and an intent-filter declaring that you can * receive ACTION_SEARCH intents. This is described in more detail in the * <a href="#SearchabilityMetadata">Searchability Metadata</a> section.</li> - * <li>Your <a href="../../../devel/bblocks-manifest.html">manifest</a> also needs a metadata entry + * <li>Your <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> also needs a metadata entry * providing a global reference to the searchable activity. This is the "glue" directing the search * UI, when invoked from any of your <i>other</i> activities, to use your application as the * default search context. This is also described in more detail in the @@ -359,7 +360,7 @@ import android.view.KeyEvent; * <li>Implement a Content Provider that provides suggestions. If you already have one, and it * has access to your suggestions data. If not, you'll have to create one. * You'll also provide information about your Content Provider in your - * package's <a href="../../../devel/bblocks-manifest.html">manifest</a>.</li> + * package's <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a>.</li> * <li>Update your searchable activity's XML configuration file. There are two categories of * information used for suggestions: * <ul><li>The first is (required) data that the search manager will @@ -634,7 +635,7 @@ import android.view.KeyEvent; * * <p><b>Metadata for searchable activity.</b> As with your search implementations described * above, you must first identify which of your activities is searchable. In the - * <a href="../../../devel/bblocks-manifest.html">manifest</a> entry for this activity, you must + * <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> entry for this activity, you must * provide two elements: * <ul><li>An intent-filter specifying that you can receive and process the * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} {@link android.content.Intent Intent}. @@ -643,7 +644,7 @@ import android.view.KeyEvent; * remaining configuration information for how your application implements search.</li></ul> * * <p>Here is a snippet showing the necessary elements in the - * <a href="../../../devel/bblocks-manifest.html">manifest</a> entry for your searchable activity. + * <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> entry for your searchable activity. * <pre class="prettyprint"> * <!-- Search Activity - searchable --> * <activity android:name="MySearchActivity" @@ -746,6 +747,14 @@ import android.view.KeyEvent; * <a href="../R.attr.html#inputType">inputType</a> attribute.</td> * <td align="center">No</td> * </tr> + * <tr><th>android:imeOptions</th> + * <td>If provided, supplies additional options for the input method. + * For most searches, in which free form text is expected, this attribute + * need not be provided, and will default to "actionSearch". + * Suitable values for this attribute are described in the + * <a href="../R.attr.html#imeOptions">imeOptions</a> attribute.</td> + * <td align="center">No</td> + * </tr> * * </tbody> * </table> @@ -765,9 +774,8 @@ import android.view.KeyEvent; * <li>.../res/values/strings.xml</li></ul> * * <p>For more complete documentation on this capability, see - * <a href="../../../devel/resources-i18n.html#AlternateResources">Resources and - * Internationalization: Supporting Alternate Resources for Alternate Languages and Configurations - * </a>. + * <a href="{@docRoot}guide/topics/resources/resources-i18n.html#AlternateResources">Resources and + * Internationalization: Alternate Resources</a>. * * <p><b>Metadata for non-searchable activities.</b> Activities which are part of a searchable * application, but don't implement search itself, require a bit of "glue" in order to cause @@ -775,7 +783,7 @@ import android.view.KeyEvent; * provided, then searches from these activities will use the system default search context. * * <p>The simplest way to specify this is to add a <i>search reference</i> element to the - * application entry in the <a href="../../../devel/bblocks-manifest.html">manifest</a> file. + * application entry in the <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> file. * The value of this reference can be either of: * <ul><li>The name of your searchable activity. * It is typically prefixed by '.' to indicate that it's in the same package.</li> @@ -803,7 +811,7 @@ import android.view.KeyEvent; * to generate search suggestions, you'll need to publish it to the system, and you'll need to * provide a bit of additional XML metadata in order to configure communications with it. * - * <p>First, in your <a href="../../../devel/bblocks-manifest.html">manifest</a>, you'll add the + * <p>First, in your <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a>, you'll add the * following lines. * <pre class="prettyprint"> * <!-- Content provider for search suggestions --> @@ -832,7 +840,7 @@ import android.view.KeyEvent; * <tbody> * <tr><th>android:searchSuggestAuthority</th> * <td>This value must match the authority string provided in the <i>provider</i> section - * of your <a href="../../../devel/bblocks-manifest.html">manifest</a>.</td> + * of your <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a>.</td> * <td align="center">Yes</td> * </tr> * diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index 6c08e75..a6a436f 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -42,12 +42,12 @@ import java.io.PrintWriter; * thread of their hosting process. This means that, if your service is going * to do any CPU intensive (such as MP3 playback) or blocking (such as * networking) operations, it should spawn its own thread in which to do that - * work. More information on this can be found in the - * <a href="{@docRoot}intro/appmodel.html#Threads">Threading section of the - * Application Model overview</a>.</p> + * work. More information on this can be found in + * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: + * Processes and Threads</a>.</p> * * <p>The Service class is an important part of an - * <a href="{@docRoot}intro/lifecycle.html">application's overall lifecycle</a>.</p> + * <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">application's overall lifecycle</a>.</p> * * <p>Topics covered here: * <ol> @@ -79,7 +79,7 @@ import java.io.PrintWriter; * to the service. The service will remain running as long as the connection * is established (whether or not the client retains a reference on the * service's IBinder). Usually the IBinder returned is for a complex - * interface that has been <a href="{@docRoot}reference/aidl.html">written + * interface that has been <a href="{@docRoot}guide/developing/tools/aidl.html">written * in aidl</a>. * * <p>A service can be both started and have connections bound to it. In such @@ -106,7 +106,7 @@ import java.io.PrintWriter; * {@link #checkCallingPermission} * method before executing the implementation of that call. * - * <p>See the <a href="{@docRoot}devel/security.html">Security Model</a> + * <p>See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> * document for more information on permissions and security in general. * * <a name="ProcessLifecycle"></a> @@ -201,14 +201,14 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * Return the communication channel to the service. May return null if * clients can not bind to the service. The returned * {@link android.os.IBinder} is usually for a complex interface - * that has been <a href="{@docRoot}reference/aidl.html">described using + * that has been <a href="{@docRoot}guide/developing/tools/aidl.html">described using * aidl</a>. * * <p><em>Note that unlike other application components, calls on to the * IBinder interface returned here may not happen on the main thread * of the process</em>. More information about this can be found - * in the <a href="{@docRoot}intro/appmodel.html#Threads">Threading section - * of the Application Model overview</a>.</p> + * in <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: + * Processes and Threads</a>.</p> * * @param intent The Intent that was used to bind to this service, * as given to {@link android.content.Context#bindService @@ -327,11 +327,15 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac } /** - * Print the Service's state into the given stream. + * Print the Service's state into the given stream. This gets invoked if + * you run "adb shell dumpsys activity service <yourservicename>". + * This is distinct from "dumpsys <servicename>", which only works for + * named system services and which invokes the {@link IBinder#dump} method + * on the {@link IBinder} interface registered with ServiceManager. * * @param fd The raw file descriptor that the dump is being sent to. * @param writer The PrintWriter to which you should dump your state. This will be - * closed for you after you return. + * closed for you after you return. * @param args additional arguments to the dump request. */ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { |