diff --git a/Android.mk b/Android.mk
index d67a21e46e897..764e88b394500 100644
--- a/Android.mk
+++ b/Android.mk
@@ -97,7 +97,9 @@ LOCAL_SRC_FILES += \
core/java/android/view/IWindowManager.aidl \
core/java/android/view/IWindowSession.aidl \
core/java/com/android/internal/app/IBatteryStats.aidl \
+ core/java/com/android/internal/app/IUsageStats.aidl \
core/java/com/android/internal/gadget/IGadgetService.aidl \
+ core/java/com/android/internal/gadget/IGadgetHost.aidl \
core/java/com/android/internal/view/IInputContext.aidl \
core/java/com/android/internal/view/IInputContextCallback.aidl \
core/java/com/android/internal/view/IInputMethod.aidl \
@@ -173,6 +175,7 @@ aidl_files := \
frameworks/base/core/java/android/view/MotionEvent.aidl \
frameworks/base/core/java/android/view/Surface.aidl \
frameworks/base/core/java/android/view/WindowManager.aidl \
+ frameworks/base/core/java/android/widget/RemoteViews.aidl \
frameworks/base/core/java/com/android/internal/view/IInputContext.aidl \
frameworks/base/core/java/com/android/internal/view/IInputMethod.aidl \
frameworks/base/core/java/com/android/internal/view/IInputMethodCallback.aidl \
diff --git a/api/3.xml b/api/3.xml
index 9d9ce98b41a04..66cdbe8b1e134 100644
--- a/api/3.xml
+++ b/api/3.xml
@@ -79027,7 +79027,7 @@
type="java.lang.String"
transient="false"
volatile="false"
- value=""name ASC""
+ value=""bucket_display_name""
static="true"
final="true"
deprecated="not deprecated"
@@ -79385,7 +79385,7 @@
type="java.lang.String"
transient="false"
volatile="false"
- value=""name ASC""
+ value=""_display_name""
static="true"
final="true"
deprecated="not deprecated"
diff --git a/api/current.xml b/api/current.xml
index 935454a19e53e..55994fb6f1d09 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -144,6 +144,17 @@
visibility="public"
>
+AndroidManifest.xml.
The Activity class is an important part of an - * application's overall lifecycle, + *
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 - * application model.
+ * part of the platform's application model. For a detailed perspective on the structure of + * Android applications and lifecycles, please read the Dev Guide document on + * Application Fundamentals. * *Topics covered here: *
See the Security Model + *
See the Security and Permissions * document for more information on permissions and security in general. * * @@ -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; } @@ -1134,12 +1140,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. + * + *
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() { } /** @@ -1443,7 +1456,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 +1487,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 +1874,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. + * + *
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. + * + *
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 +1968,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 +1986,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 +2006,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; } @@ -2864,6 +2902,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. + * + *
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
@@ -3482,7 +3549,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 f9b92218c56a9..07520c9d65f7d 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 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 ae9f3bf15029c..f11dbec3b422e 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;
@@ -82,6 +83,17 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return gDefault;
}
+ /**
+ * 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,23 @@ 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 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 +1633,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 +2071,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 +2119,17 @@ 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;
+ }
private IBinder mRemote;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e4c1057e3878d..bf5616e55a372 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(
@@ -1462,7 +1471,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 +1583,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:",
@@ -1751,6 +1850,7 @@ public final class ActivityThread {
final HashMap 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. Managing focus and knowing if Search is active. 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
- * Activity Lifecycle. The search UI is
+ * Application Fundamentals:
+ * Activity Lifecycle. 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:
* When the search UI appears, your activity will lose input focus.
@@ -212,11 +213,11 @@ import android.view.KeyEvent;
* {@link #QUERY getStringExtra(SearchManager.QUERY)}.
* Metadata for searchable activity. As with your search implementations described
* above, you must first identify which of your activities is searchable. In the
- * manifest entry for this activity, you must
+ * manifest entry for this activity, you must
* provide two elements:
* Here is a snippet showing the necessary elements in the
- * manifest entry for your searchable activity.
+ * manifest entry for your searchable activity.
* For more complete documentation on this capability, see
- * Resources and
- * Internationalization: Supporting Alternate Resources for Alternate Languages and Configurations
- * .
+ * Resources and
+ * Internationalization: Alternate Resources.
*
* Metadata for non-searchable activities. 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 +775,7 @@ import android.view.KeyEvent;
* provided, then searches from these activities will use the system default search context.
*
* The simplest way to specify this is to add a search reference element to the
- * application entry in the manifest file.
+ * application entry in the manifest file.
* The value of this reference can be either of:
* First, in your manifest, you'll add the
+ * First, in your manifest, you'll add the
* following lines.
* The Service class is an important part of an
- * application's overall lifecycle. Topics covered here:
* 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.
*
- * See the Security Model
+ * See the Security and Permissions
* document for more information on permissions and security in general.
*
*
@@ -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 described using
+ * that has been described using
* aidl.
*
* Note that unlike other application components, calls on to the
* IBinder interface returned here may not happen on the main thread
* of the process. More information about this can be found
- * in the Threading section
- * of the Application Model overview. My include the following extras:
+ * The currently running instance of task B in the above example will
- * either receiving the new intent you are starting here in its
- * onNewIntent() method, or be itself finished and restarting with the
+ * either receive the new intent you are starting here in its
+ * onNewIntent() method, or be itself finished and restarted with the
* new intent. If it has declared its launch mode to be "multiple" (the
* default) it will be finished and re-created; for all other launch modes
* it will receive the Intent in the current instance.
@@ -1855,7 +1901,7 @@ public class Intent implements Parcelable {
*/
public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 0x00080000;
/**
- * If set, this flag will prevent the normal {@link android.app.Activity#onUserLeaving}
+ * If set, this flag will prevent the normal {@link android.app.Activity#onUserLeaveHint}
* callback from occurring on the current frontmost activity before it is
* paused as the newly-started activity is brought to the front.
*
@@ -1871,12 +1917,39 @@ public class Intent implements Parcelable {
* activity does not think the user has acknowledged its notification.
*/
public static final int FLAG_ACTIVITY_NO_USER_ACTION = 0x00040000;
-
+ /**
+ * If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
+ * this flag will cause the launched activity to be brought to the front of its
+ * task's history stack if it is already running.
+ *
+ * For example, consider a task consisting of four activities: A, B, C, D.
+ * If D calls startActivity() with an Intent that resolves to the component
+ * of activity B, then B will be brought to the front of the history stack,
+ * with this resulting order: A, C, D, B.
+ *
+ * This flag will be ignored if {@link #FLAG_ACTIVITY_CLEAR_TOP} is also
+ * specified.
+ */
+ public static final int FLAG_ACTIVITY_REORDER_TO_FRONT = 0X00020000;
/**
* If set, when sending a broadcast only registered receivers will be
* called -- no BroadcastReceiver components will be launched.
*/
public static final int FLAG_RECEIVER_REGISTERED_ONLY = 0x40000000;
+ /**
+ * If set, when sending a broadcast before boot has completed only
+ * registered receivers will be called -- no BroadcastReceiver components
+ * will be launched. Sticky intent state will be recorded properly even
+ * if no receivers wind up being called. If {@link #FLAG_RECEIVER_REGISTERED_ONLY}
+ * is specified in the broadcast intent, this flag is unnecessary.
+ *
+ * This flag is only for use by system sevices as a convenience to
+ * avoid having to implement a more complex mechanism around detection
+ * of boot completion.
+ *
+ * @hide
+ */
+ public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x20000000;
// ---------------------------------------------------------------------
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 6bc3774859b0d..96470c3c5756c 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -123,7 +123,7 @@ class SyncManager {
private static final String SYNC_WAKE_LOCK = "SyncManagerSyncWakeLock";
private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarmWakeLock";
-
+
private Context mContext;
private ContentResolver mContentResolver;
@@ -249,7 +249,7 @@ class SyncManager {
mSyncQueue = new SyncQueue(mSyncStorageEngine);
mContext = context;
-
+
mSyncThread = new HandlerThread("SyncHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
mSyncThread.start();
mSyncHandler = new SyncHandler(mSyncThread.getLooper());
@@ -489,7 +489,7 @@ class SyncManager {
// Require the precise value "yes" to discourage accidental activation.
return "yes".equals(SystemProperties.get("ro.config.sync"));
}
-
+
/**
* Initiate a sync. This can start a sync for all providers
* (pass null to url, set onlyTicklable to false), only those
@@ -515,7 +515,7 @@ class SyncManager {
* syncs of a specific provider. Can be null. Is ignored
* if the url is null.
* @param delay how many milliseconds in the future to wait before performing this
- * sync. -1 means to make this the next sync to perform.
+ * sync. -1 means to make this the next sync to perform.
*/
public void scheduleSync(Uri url, Bundle extras, long delay) {
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
@@ -694,7 +694,7 @@ class SyncManager {
class SyncHandlerMessagePayload {
public final ActiveSyncContext activeSyncContext;
public final SyncResult syncResult;
-
+
SyncHandlerMessagePayload(ActiveSyncContext syncContext, SyncResult syncResult) {
this.activeSyncContext = syncContext;
this.syncResult = syncResult;
@@ -740,7 +740,7 @@ class SyncManager {
if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) {
newDelayInMs = maxSyncRetryTimeInSeconds * 1000;
}
-
+
SyncOperation rescheduledSyncOperation = new SyncOperation(syncOperation);
rescheduledSyncOperation.setDelay(newDelayInMs);
scheduleSyncOperation(rescheduledSyncOperation);
@@ -786,7 +786,7 @@ class SyncManager {
// key than the one we are scheduling.
if (!activeIsExpedited && !hasSameKey) {
rescheduleImmediately(activeSyncContext.mSyncOperation);
- sendSyncFinishedOrCanceledMessage(activeSyncContext,
+ sendSyncFinishedOrCanceledMessage(activeSyncContext,
null /* no result since this is a cancel */);
}
}
@@ -1323,7 +1323,7 @@ class SyncManager {
public SyncHandler(Looper looper) {
super(looper);
}
-
+
public void handleMessage(Message msg) {
handleSyncHandlerMessage(msg);
}
@@ -1462,6 +1462,9 @@ class SyncManager {
// start it, otherwise just get out.
SyncOperation syncOperation;
final Sync.Settings.QueryMap syncSettings = getSyncSettings();
+ final ConnectivityManager connManager = (ConnectivityManager)
+ mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ final boolean backgroundDataSetting = connManager.getBackgroundDataSetting();
synchronized (mSyncQueue) {
while (true) {
syncOperation = mSyncQueue.head();
@@ -1484,10 +1487,10 @@ class SyncManager {
// skip the sync if it isn't a force and the settings are off for this provider
final boolean force = syncOperation.extras.getBoolean(
ContentResolver.SYNC_EXTRAS_FORCE, false);
- if (!force && (!syncSettings.getBackgroundData()
+ if (!force && (!backgroundDataSetting
|| !syncSettings.getListenForNetworkTickles()
|| !syncSettings.getSyncProviderAutomatically(
- syncOperation.authority))) {
+ syncOperation.authority))) {
if (isLoggable) {
Log.v(TAG, "runStateIdle: sync off, dropping " + syncOperation);
}
@@ -1669,7 +1672,7 @@ class SyncManager {
* @param syncResult the SyncResult from which to read
* @return the most "serious" error set in the SyncResult
* @throws IllegalStateException if the SyncResult does not indicate any errors.
- * If SyncResult.error() is true then it is safe to call this.
+ * If SyncResult.error() is true then it is safe to call this.
*/
private int syncResultToErrorNumber(SyncResult syncResult) {
if (syncResult.syncAlreadyInProgress) return History.ERROR_SYNC_ALREADY_IN_PROGRESS;
@@ -1679,7 +1682,8 @@ class SyncManager {
if (syncResult.stats.numConflictDetectedExceptions > 0) return History.ERROR_CONFLICT;
if (syncResult.tooManyDeletions) return History.ERROR_TOO_MANY_DELETIONS;
if (syncResult.tooManyRetries) return History.ERROR_TOO_MANY_RETRIES;
- throw new IllegalStateException("we are not in an error state, " + toString());
+ if (syncResult.databaseError) return History.ERROR_INTERNAL;
+ throw new IllegalStateException("we are not in an error state, " + syncResult);
}
private void manageSyncNotification() {
@@ -1717,7 +1721,7 @@ class SyncManager {
if (mSyncNotificationInfo.isActive) {
shouldInstall = shouldCancel;
} else {
- final boolean timeToShowNotification =
+ final boolean timeToShowNotification =
now > mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY;
final boolean syncIsForced = syncOperation.extras
.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false);
@@ -1769,7 +1773,7 @@ class SyncManager {
if (!mDataConnectionIsConnected) return;
if (mAccounts == null) return;
if (mStorageIsLow) return;
-
+
// Compute the alarm fire time:
// - not syncing: time of the next sync operation
// - syncing, no notification: time from sync start to notification create time
@@ -1850,12 +1854,12 @@ class SyncManager {
clickIntent.putExtra("account", account);
clickIntent.putExtra("provider", authority);
clickIntent.putExtra("numDeletes", numDeletes);
-
+
if (!isActivityAvailable(clickIntent)) {
Log.w(TAG, "No activity found to handle too many deletes.");
return;
}
-
+
final PendingIntent pendingIntent = PendingIntent
.getActivity(mContext, 0, clickIntent, PendingIntent.FLAG_CANCEL_CURRENT);
@@ -1877,7 +1881,7 @@ class SyncManager {
/**
* Checks whether an activity exists on the system image for the given intent.
- *
+ *
* @param intent The intent for an activity.
* @return Whether or not an activity exists.
*/
@@ -1892,10 +1896,10 @@ class SyncManager {
return true;
}
}
-
+
return false;
}
-
+
public long insertStartSyncEvent(SyncOperation syncOperation) {
final int source = syncOperation.syncSource;
final long now = System.currentTimeMillis();
diff --git a/core/java/android/content/TempProviderSyncAdapter.java b/core/java/android/content/TempProviderSyncAdapter.java
index 78510aad987a0..eb3a5da4a5b12 100644
--- a/core/java/android/content/TempProviderSyncAdapter.java
+++ b/core/java/android/content/TempProviderSyncAdapter.java
@@ -1,11 +1,11 @@
package android.content;
-import com.google.android.net.NetStats;
-
import android.database.SQLException;
import android.os.Bundle;
import android.os.Debug;
+import android.os.NetStat;
import android.os.Parcelable;
+import android.os.Process;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Config;
@@ -177,7 +177,8 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter {
private final Bundle mExtras;
private final SyncContext mSyncContext;
private volatile boolean mIsCanceled = false;
- private long[] mNetStats;
+ private long mInitialTxBytes;
+ private long mInitialRxBytes;
private final SyncResult mResult;
SyncThread(SyncContext syncContext, String account, Bundle extras) {
@@ -193,15 +194,18 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter {
if (mAdapterSyncStarted) onSyncCanceled();
if (mProviderSyncStarted) mProvider.onSyncCanceled();
// We may lose the last few sync events when canceling. Oh well.
- long[] newNetStats = NetStats.getStats();
- logSyncDetails(newNetStats[0] - mNetStats[0], newNetStats[1] - mNetStats[1], mResult);
+ int uid = Process.myUid();
+ logSyncDetails(NetStat.getUidTxBytes(uid) - mInitialTxBytes,
+ NetStat.getUidRxBytes(uid) - mInitialRxBytes, mResult);
}
@Override
public void run() {
- android.os.Process.setThreadPriority(android.os.Process.myTid(),
- android.os.Process.THREAD_PRIORITY_BACKGROUND);
- mNetStats = NetStats.getStats();
+ Process.setThreadPriority(Process.myTid(),
+ Process.THREAD_PRIORITY_BACKGROUND);
+ int uid = Process.myUid();
+ mInitialTxBytes = NetStat.getUidTxBytes(uid);
+ mInitialRxBytes = NetStat.getUidRxBytes(uid);
try {
sync(mSyncContext, mAccount, mExtras);
} catch (SQLException e) {
@@ -210,8 +214,8 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter {
} finally {
mSyncThread = null;
if (!mIsCanceled) {
- long[] newNetStats = NetStats.getStats();
- logSyncDetails(newNetStats[0] - mNetStats[0], newNetStats[1] - mNetStats[1], mResult);
+ logSyncDetails(NetStat.getUidTxBytes(uid) - mInitialTxBytes,
+ NetStat.getUidRxBytes(uid) - mInitialRxBytes, mResult);
mSyncContext.onFinished(mResult);
}
}
diff --git a/core/java/android/content/pm/ConfigurationInfo.java b/core/java/android/content/pm/ConfigurationInfo.java
index 9115225c4415e..dcc746331b0b8 100755
--- a/core/java/android/content/pm/ConfigurationInfo.java
+++ b/core/java/android/content/pm/ConfigurationInfo.java
@@ -16,7 +16,6 @@
package android.content.pm;
-import android.content.res.Configuration;
import android.os.Parcel;
import android.os.Parcelable;
@@ -60,12 +59,12 @@ public class ConfigurationInfo implements Parcelable {
/**
* Value for {@link #reqInputFeatures}: if set, indicates that the application
- * requires a hard keyboard
+ * requires a five way navigation device
*/
public static final int INPUT_FEATURE_FIVE_WAY_NAV = 0x00000002;
/**
- * Flags associated with the application. Any combination of
+ * Flags associated with the input features. Any combination of
* {@link #INPUT_FEATURE_HARD_KEYBOARD},
* {@link #INPUT_FEATURE_FIVE_WAY_NAV}
*/
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index ea86188d2bf9b..d3f6f3c564c89 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -255,8 +255,15 @@ interface IPackageManager {
* retrieval of information is complete.
*/
void getPackageSizeInfo(in String packageName, IPackageStatsObserver observer);
+
+ /**
+ * Get a list of shared libraries that are available on the
+ * system.
+ */
+ String[] getSystemSharedLibraryNames();
void enterSafeMode();
+ boolean isSafeMode();
void systemReady();
boolean hasSystemUidErrors();
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4b902e9fe944f..698f27fc0a3fc 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -851,6 +851,16 @@ public abstract class PackageManager {
* @see #GET_UNINSTALLED_PACKAGES
*/
public abstract List For an example, see the NotePadProvider class in the NotePad sample application,
+ * in the samples/ directory of the SDK. This may be sent in response to a new instance for this gadget provider having
+ * been instantiated, the requested {@link GadgetInfo#updatePeriodMillis update interval}
+ * having lapsed, or the system booting.
*/
public static final String GADGET_UPDATE_ACTION = "android.gadget.action.GADGET_UPDATE";
/**
- * Sent when the gadget is added to a host for the first time. TODO: Maybe we don't want this.
+ * Sent when it is time to configure your gadget. This action is not sent as a broadcast
+ * to the gadget provider, but as a startActivity to the activity specified in the
+ * {@link GadgetInfo GadgetInfo meta-data}.
+ *
+ * The {@link #EXTRA_GADGET_ID} extra contains the gadget ID.
*/
- public static final String GADGET_ENABLE_ACTION = "android.gadget.action.GADGET_ENABLE";
+ public static final String GADGET_CONFIGURE_ACTION = "android.gadget.action.GADGET_CONFIGURE";
/**
- * Sent when the gadget is removed from the last host. TODO: Maybe we don't want this.
+ * Sent when the gadget is added to a host for the first time. This broadcast is sent at
+ * boot time if there is a gadget host installed with an instance for this provider.
*/
- public static final String GADGET_DISABLE_ACTION = "android.gadget.action.GADGET_DISABLE";
+ public static final String GADGET_ENABLED_ACTION = "android.gadget.action.GADGET_ENABLED";
+
+ /**
+ * Sent when an instances of a gadget is deleted from the host.
+ */
+ public static final String GADGET_DELETED_ACTION = "android.gadget.action.GADGET_DELETED";
+
+ /**
+ * Sent when the gadget is removed from the last host.
+ */
+ public static final String GADGET_DISABLED_ACTION = "android.gadget.action.GADGET_DISABLED";
/**
* Field for the manifest meta-data tag.
@@ -106,10 +130,36 @@ public class GadgetManager {
*
* This method will only work when called from the uid that owns the gadget provider.
*
- * @param gadgetId The gadget instance for which to set the RemoteViews.
+ * @param gadgetIds The gadget instances for which to set the RemoteViews.
* @param views The RemoteViews object to show.
*/
- public void updateGadget(int gadgetId, RemoteViews views) {
+ public void updateGadget(int[] gadgetIds, RemoteViews views) {
+ try {
+ sService.updateGadgetIds(gadgetIds, views);
+ }
+ catch (RemoteException e) {
+ throw new RuntimeException("system server dead?", e);
+ }
+ }
+
+ /**
+ * Call this with the new RemoteViews for your gadget whenever you need to.
+ *
+ *
+ * This method will only work when called from the uid that owns the gadget provider.
+ *
+ * @param provider The {@link ComponentName} for the {@link
+ * android.content.BroadcastReceiver BroadcastReceiver} provider
+ * for your gadget.
+ * @param views The RemoteViews object to show.
+ */
+ public void updateGadget(ComponentName provider, RemoteViews views) {
+ try {
+ sService.updateGadgetProvider(provider, views);
+ }
+ catch (RemoteException e) {
+ throw new RuntimeException("system server dead?", e);
+ }
}
/**
@@ -139,32 +189,6 @@ public class GadgetManager {
}
}
- /**
- * Get a gadgetId for a host in the calling process.
- *
- * @return a gadgetId
- */
- public int allocateGadgetId(String hostPackage) {
- try {
- return sService.allocateGadgetId(hostPackage);
- }
- catch (RemoteException e) {
- throw new RuntimeException("system server dead?", e);
- }
- }
-
- /**
- * Delete the gadgetId. Same as removeGadget on GadgetHost.
- */
- public void deleteGadgetId(int gadgetId) {
- try {
- sService.deleteGadgetId(gadgetId);
- }
- catch (RemoteException e) {
- throw new RuntimeException("system server dead?", e);
- }
- }
-
/**
* Set the component for a given gadgetId. You need the GADGET_LIST permission.
*/
diff --git a/core/java/android/gadget/GadgetProvider.java b/core/java/android/gadget/GadgetProvider.java
new file mode 100755
index 0000000000000..1ddfe3f0babf3
--- /dev/null
+++ b/core/java/android/gadget/GadgetProvider.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2006 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.gadget;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * A conveience class to aid in implementing a gadget provider.
+ * Everything you can do with GadgetProvider, you can do with a regular {@link BroadcastReceiver}.
+ * GadgetProvider merely parses the relevant fields out of the Intent that is received in
+ * {@link #onReceive(Context,Intent) onReceive(Context,Intent)}, and calls hook methods
+ * with the received extras.
+ *
+ * Extend this class and override one or more of the {@link #onUpdate}, {@link #onDeleted},
+ * {@link #onEnabled} or {@link #onDisabled} methods to implement your own gadget functionality.
+ *
+ * If you want this method called, you must declare in an intent-filter in
+ * your AndroidManifest.xml file that you accept the GADGET_UPDATE_ACTION intent action.
+ * For example:
+ * TODO: SAMPLE CODE GOES HERE
+ * If you want this method called, you must declare in an intent-filter in
+ * your AndroidManifest.xml file that you accept the GADGET_DELETED_ACTION intent action.
+ * For example:
+ * TODO: SAMPLE CODE GOES HERE
+ * If you want this method called, you must declare in an intent-filter in
+ * your AndroidManifest.xml file that you accept the GADGET_ENABLED_ACTION intent action.
+ * For example:
+ * TODO: SAMPLE CODE GOES HERE
+ * If you want this method called, you must declare in an intent-filter in
+ * your AndroidManifest.xml file that you accept the GADGET_DISABLED_ACTION intent action.
+ * For example:
+ * TODO: SAMPLE CODE GOES HERE
+ * Android allows applications to publish views to be embedded in other applications. These
+views are called gadgets, and are published by "gadget providers." The component that can
+contain gadgets is called a "gadget host." See the links below for more information.
+ Any application can publish gadgets. All an application needs to do to publish a gadget is
+to have a {@link android.content.BroadcastReceiver} that receives the {@link
+android.gadget.GadgetManager#GADGET_UPDATE_ACTION GadgetManager.GADGET_UPDATE_ACTION} intent,
+and provide some meta-data about the gadget.
+
+ {@link GadgetProvider} is just a convenience class. If you would like to receive the
+gadget broadcasts directly, you can. By way of example, the implementation of
+{@link GadgetProvider.onReceive} is quite simple: Gadget hosts are the containers in which gadgets can be placed. Most of the look and feel
+details are left up to the gadget hosts. For example, the home screen has one way of viewing
+gadgets, but the lock screen could also contain gadgets, and it would have a different way of
+adding, removing and otherwise managing gadgets. For more information on implementing your own gadget host, see the
+{@link android.gadget.GadgetHost GadgetHost} class. This uses the World Magnetic Model produced by the United States National
+ * Geospatial-Intelligence Agency. More details about the model can be found at
+ * http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml.
+ * This class currently uses WMM-2005 which is valid until 2010, but should
+ * produce acceptable results for several years after that.
+ */
+public class GeomagneticField {
+ // The magnetic field at a given point, in nonoteslas in geodetic
+ // coordinates.
+ private float mX;
+ private float mY;
+ private float mZ;
+
+ // Geocentric coordinates -- set by computeGeocentricCoordinates.
+ private float mGcLatitudeRad;
+ private float mGcLongitudeRad;
+ private float mGcRadiusKm;
+
+ // Constants from WGS84 (the coordinate system used by GPS)
+ static private final float EARTH_SEMI_MAJOR_AXIS_KM = 6378.137f;
+ static private final float EARTH_SEMI_MINOR_AXIS_KM = 6356.7523f;
+ static private final float EARTH_REFERENCE_RADIUS_KM = 6371.2f;
+
+ // These coefficients and the formulae used below are from:
+ // NOAA Technical Report: The US/UK World Magnetic Model for 2005-2010
+ static private final float[][] G_COEFF = new float[][] {
+ { 0f },
+ { -29556.8f, -1671.7f },
+ { -2340.6f, 3046.9f, 1657.0f },
+ { 1335.4f, -2305.1f, 1246.7f, 674.0f },
+ { 919.8f, 798.1f, 211.3f, -379.4f, 100.0f },
+ { -227.4f, 354.6f, 208.7f, -136.5f, -168.3f, -14.1f },
+ { 73.2f, 69.7f, 76.7f, -151.2f, -14.9f, 14.6f, -86.3f },
+ { 80.1f, -74.5f, -1.4f, 38.5f, 12.4f, 9.5f, 5.7f, 1.8f },
+ { 24.9f, 7.7f, -11.6f, -6.9f, -18.2f, 10.0f, 9.2f, -11.6f, -5.2f },
+ { 5.6f, 9.9f, 3.5f, -7.0f, 5.1f, -10.8f, -1.3f, 8.8f, -6.7f, -9.1f },
+ { -2.3f, -6.3f, 1.6f, -2.6f, 0.0f, 3.1f, 0.4f, 2.1f, 3.9f, -0.1f, -2.3f },
+ { 2.8f, -1.6f, -1.7f, 1.7f, -0.1f, 0.1f, -0.7f, 0.7f, 1.8f, 0.0f, 1.1f, 4.1f },
+ { -2.4f, -0.4f, 0.2f, 0.8f, -0.3f, 1.1f, -0.5f, 0.4f, -0.3f, -0.3f, -0.1f,
+ -0.3f, -0.1f } };
+
+ static private final float[][] H_COEFF = new float[][] {
+ { 0f },
+ { 0.0f, 5079.8f },
+ { 0.0f, -2594.7f, -516.7f },
+ { 0.0f, -199.9f, 269.3f, -524.2f },
+ { 0.0f, 281.5f, -226.0f, 145.8f, -304.7f },
+ { 0.0f, 42.4f, 179.8f, -123.0f, -19.5f, 103.6f },
+ { 0.0f, -20.3f, 54.7f, 63.6f, -63.4f, -0.1f, 50.4f },
+ { 0.0f, -61.5f, -22.4f, 7.2f, 25.4f, 11.0f, -26.4f, -5.1f },
+ { 0.0f, 11.2f, -21.0f, 9.6f, -19.8f, 16.1f, 7.7f, -12.9f, -0.2f },
+ { 0.0f, -20.1f, 12.9f, 12.6f, -6.7f, -8.1f, 8.0f, 2.9f, -7.9f, 6.0f },
+ { 0.0f, 2.4f, 0.2f, 4.4f, 4.8f, -6.5f, -1.1f, -3.4f, -0.8f, -2.3f, -7.9f },
+ { 0.0f, 0.3f, 1.2f, -0.8f, -2.5f, 0.9f, -0.6f, -2.7f, -0.9f, -1.3f, -2.0f, -1.2f },
+ { 0.0f, -0.4f, 0.3f, 2.4f, -2.6f, 0.6f, 0.3f, 0.0f, 0.0f, 0.3f, -0.9f, -0.4f,
+ 0.8f } };
+
+ static private final float[][] DELTA_G = new float[][] {
+ { 0f },
+ { 8.0f, 10.6f },
+ { -15.1f, -7.8f, -0.8f },
+ { 0.4f, -2.6f, -1.2f, -6.5f },
+ { -2.5f, 2.8f, -7.0f, 6.2f, -3.8f },
+ { -2.8f, 0.7f, -3.2f, -1.1f, 0.1f, -0.8f },
+ { -0.7f, 0.4f, -0.3f, 2.3f, -2.1f, -0.6f, 1.4f },
+ { 0.2f, -0.1f, -0.3f, 1.1f, 0.6f, 0.5f, -0.4f, 0.6f },
+ { 0.1f, 0.3f, -0.4f, 0.3f, -0.3f, 0.2f, 0.4f, -0.7f, 0.4f },
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } };
+
+ static private final float[][] DELTA_H = new float[][] {
+ { 0f },
+ { 0.0f, -20.9f },
+ { 0.0f, -23.2f, -14.6f },
+ { 0.0f, 5.0f, -7.0f, -0.6f },
+ { 0.0f, 2.2f, 1.6f, 5.8f, 0.1f },
+ { 0.0f, 0.0f, 1.7f, 2.1f, 4.8f, -1.1f },
+ { 0.0f, -0.6f, -1.9f, -0.4f, -0.5f, -0.3f, 0.7f },
+ { 0.0f, 0.6f, 0.4f, 0.2f, 0.3f, -0.8f, -0.2f, 0.1f },
+ { 0.0f, -0.2f, 0.1f, 0.3f, 0.4f, 0.1f, -0.2f, 0.4f, 0.4f },
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } };
+
+ static private final long BASE_TIME =
+ new GregorianCalendar(2005, 1, 1).getTimeInMillis();
+
+ // The ratio between the Gauss-normalized associated Legendre functions and
+ // the Schmid quasi-normalized ones. Compute these once staticly since they
+ // don't depend on input variables at all.
+ static private final float[][] SCHMIDT_QUASI_NORM_FACTORS =
+ computeSchmidtQuasiNormFactors(G_COEFF.length);
+
+ /**
+ * Estimate the magnetic field at a given point and time.
+ *
+ * @param gdLatitudeDeg
+ * Latitude in WGS84 geodetic coordinates -- positive is east.
+ * @param gdLongitudeDeg
+ * Longitude in WGS84 geodetic coordinates -- positive is north.
+ * @param altitudeMeters
+ * Altitude in WGS84 geodetic coordinates, in meters.
+ * @param timeMillis
+ * Time at which to evaluate the declination, in milliseconds
+ * since January 1, 1970. (approximate is fine -- the declination
+ * changes very slowly).
+ */
+ public GeomagneticField(float gdLatitudeDeg,
+ float gdLongitudeDeg,
+ float altitudeMeters,
+ long timeMillis) {
+ final int MAX_N = G_COEFF.length; // Maximum degree of the coefficients.
+
+ // We don't handle the north and south poles correctly -- pretend that
+ // we're not quite at them to avoid crashing.
+ gdLatitudeDeg = Math.min(90.0f - 1e-5f,
+ Math.max(-90.0f + 1e-5f, gdLatitudeDeg));
+ computeGeocentricCoordinates(gdLatitudeDeg,
+ gdLongitudeDeg,
+ altitudeMeters);
+
+ assert G_COEFF.length == H_COEFF.length;
+
+ // Note: LegendreTable computes associated Legendre functions for
+ // cos(theta). We want the associated Legendre functions for
+ // sin(latitude), which is the same as cos(PI/2 - latitude), except the
+ // derivate will be negated.
+ LegendreTable legendre =
+ new LegendreTable(MAX_N - 1,
+ (float) (Math.PI / 2.0 - mGcLatitudeRad));
+
+ // Compute a table of (EARTH_REFERENCE_RADIUS_KM / radius)^n for i in
+ // 0..MAX_N-2 (this is much faster than calling Math.pow MAX_N+1 times).
+ float[] relativeRadiusPower = new float[MAX_N + 2];
+ relativeRadiusPower[0] = 1.0f;
+ relativeRadiusPower[1] = EARTH_REFERENCE_RADIUS_KM / mGcRadiusKm;
+ for (int i = 2; i < relativeRadiusPower.length; ++i) {
+ relativeRadiusPower[i] = relativeRadiusPower[i - 1] *
+ relativeRadiusPower[1];
+ }
+
+ // Compute tables of sin(lon * m) and cos(lon * m) for m = 0..MAX_N --
+ // this is much faster than calling Math.sin and Math.com MAX_N+1 times.
+ float[] sinMLon = new float[MAX_N];
+ float[] cosMLon = new float[MAX_N];
+ sinMLon[0] = 0.0f;
+ cosMLon[0] = 1.0f;
+ sinMLon[1] = (float) Math.sin(mGcLongitudeRad);
+ cosMLon[1] = (float) Math.cos(mGcLongitudeRad);
+
+ for (int m = 2; m < MAX_N; ++m) {
+ // Standard expansions for sin((m-x)*theta + x*theta) and
+ // cos((m-x)*theta + x*theta).
+ int x = m >> 1;
+ sinMLon[m] = sinMLon[m-x] * cosMLon[x] + cosMLon[m-x] * sinMLon[x];
+ cosMLon[m] = cosMLon[m-x] * cosMLon[x] - sinMLon[m-x] * sinMLon[x];
+ }
+
+ float inverseCosLatitude = 1.0f / (float) Math.cos(mGcLatitudeRad);
+ float yearsSinceBase =
+ (timeMillis - BASE_TIME) / (365f * 24f * 60f * 60f * 1000f);
+
+ // We now compute the magnetic field strength given the geocentric
+ // location. The magnetic field is the derivative of the potential
+ // function defined by the model. See NOAA Technical Report: The US/UK
+ // World Magnetic Model for 2005-2010 for the derivation.
+ float gcX = 0.0f; // Geocentric northwards component.
+ float gcY = 0.0f; // Geocentric eastwards component.
+ float gcZ = 0.0f; // Geocentric downwards component.
+
+ for (int n = 1; n < MAX_N; n++) {
+ for (int m = 0; m <= n; m++) {
+ // Adjust the coefficients for the current date.
+ float g = G_COEFF[n][m] + yearsSinceBase * DELTA_G[n][m];
+ float h = H_COEFF[n][m] + yearsSinceBase * DELTA_H[n][m];
+
+ // Negative derivative with respect to latitude, divided by
+ // radius. This looks like the negation of the version in the
+ // NOAA Techincal report because that report used
+ // P_n^m(sin(theta)) and we use P_n^m(cos(90 - theta)), so the
+ // derivative with respect to theta is negated.
+ gcX += relativeRadiusPower[n+2]
+ * (g * cosMLon[m] + h * sinMLon[m])
+ * legendre.mPDeriv[n][m]
+ * SCHMIDT_QUASI_NORM_FACTORS[n][m];
+
+ // Negative derivative with respect to longitude, divided by
+ // radius.
+ gcY += relativeRadiusPower[n+2] * m
+ * (g * sinMLon[m] - h * cosMLon[m])
+ * legendre.mP[n][m]
+ * SCHMIDT_QUASI_NORM_FACTORS[n][m]
+ * inverseCosLatitude;
+
+ // Negative derivative with respect to radius.
+ gcZ -= (n + 1) * relativeRadiusPower[n+2]
+ * (g * cosMLon[m] + h * sinMLon[m])
+ * legendre.mP[n][m]
+ * SCHMIDT_QUASI_NORM_FACTORS[n][m];
+ }
+ }
+
+ // Convert back to geodetic coordinates. This is basically just a
+ // rotation around the Y-axis by the difference in latitudes between the
+ // geocentric frame and the geodetic frame.
+ double latDiffRad = Math.toRadians(gdLatitudeDeg) - mGcLatitudeRad;
+ mX = (float) (gcX * Math.cos(latDiffRad)
+ + gcZ * Math.sin(latDiffRad));
+ mY = gcY;
+ mZ = (float) (- gcX * Math.sin(latDiffRad)
+ + gcZ * Math.cos(latDiffRad));
+ }
+
+ /**
+ * @return The X (northward) component of the magnetic field in nanoteslas.
+ */
+ public float getX() {
+ return mX;
+ }
+
+ /**
+ * @return The Y (eastward) component of the magnetic field in nanoteslas.
+ */
+ public float getY() {
+ return mY;
+ }
+
+ /**
+ * @return The Z (downward) component of the magnetic field in nanoteslas.
+ */
+ public float getZ() {
+ return mZ;
+ }
+
+ /**
+ * @return The declination of the horizontal component of the magnetic
+ * field from true north, in degrees (i.e. positive means the
+ * magnetic field is rotated east that much from true north).
+ */
+ public float getDeclination() {
+ return (float) Math.toDegrees(Math.atan2(mY, mX));
+ }
+
+ /**
+ * @return The inclination of the magnetic field in degrees -- positive
+ * means the magnetic field is rotated downwards.
+ */
+ public float getInclination() {
+ return (float) Math.toDegrees(Math.atan2(mZ,
+ getHorizontalStrength()));
+ }
+
+ /**
+ * @return Horizontal component of the field strength in nonoteslas.
+ */
+ public float getHorizontalStrength() {
+ return (float) Math.sqrt(mX * mX + mY * mY);
+ }
+
+ /**
+ * @return Total field strength in nanoteslas.
+ */
+ public float getFieldStrength() {
+ return (float) Math.sqrt(mX * mX + mY * mY + mZ * mZ);
+ }
+
+ /**
+ * @param gdLatitudeDeg
+ * Latitude in WGS84 geodetic coordinates.
+ * @param gdLongitudeDeg
+ * Longitude in WGS84 geodetic coordinates.
+ * @param altitudeMeters
+ * Altitude above sea level in WGS84 geodetic coordinates.
+ * @return Geocentric latitude (i.e. angle between closest point on the
+ * equator and this point, at the center of the earth.
+ */
+ private void computeGeocentricCoordinates(float gdLatitudeDeg,
+ float gdLongitudeDeg,
+ float altitudeMeters) {
+ float altitudeKm = altitudeMeters / 1000.0f;
+ float a2 = EARTH_SEMI_MAJOR_AXIS_KM * EARTH_SEMI_MAJOR_AXIS_KM;
+ float b2 = EARTH_SEMI_MINOR_AXIS_KM * EARTH_SEMI_MINOR_AXIS_KM;
+ double gdLatRad = Math.toRadians(gdLatitudeDeg);
+ float clat = (float) Math.cos(gdLatRad);
+ float slat = (float) Math.sin(gdLatRad);
+ float tlat = slat / clat;
+ float latRad =
+ (float) Math.sqrt(a2 * clat * clat + b2 * slat * slat);
+
+ mGcLatitudeRad = (float) Math.atan(tlat * (latRad * altitudeKm + b2)
+ / (latRad * altitudeKm + a2));
+
+ mGcLongitudeRad = (float) Math.toRadians(gdLongitudeDeg);
+
+ float radSq = altitudeKm * altitudeKm
+ + 2 * altitudeKm * (float) Math.sqrt(a2 * clat * clat +
+ b2 * slat * slat)
+ + (a2 * a2 * clat * clat + b2 * b2 * slat * slat)
+ / (a2 * clat * clat + b2 * slat * slat);
+ mGcRadiusKm = (float) Math.sqrt(radSq);
+ }
+
+
+ /**
+ * Utility class to compute a table of Gauss-normalized associated Legendre
+ * functions P_n^m(cos(theta))
+ */
+ static private class LegendreTable {
+ // These are the Gauss-normalized associated Legendre functions -- that
+ // is, they are normal Legendre functions multiplied by
+ // (n-m)!/(2n-1)!! (where (2n-1)!! = 1*3*5*...*2n-1)
+ public final float[][] mP;
+
+ // Derivative of mP, with respect to theta.
+ public final float[][] mPDeriv;
+
+ /**
+ * @param maxN
+ * The maximum n- and m-values to support
+ * @param thetaRad
+ * Returned functions will be Gauss-normalized
+ * P_n^m(cos(thetaRad)), with thetaRad in radians.
+ */
+ public LegendreTable(int maxN, float thetaRad) {
+ // Compute the table of Gauss-normalized associated Legendre
+ // functions using standard recursion relations. Also compute the
+ // table of derivatives using the derivative of the recursion
+ // relations.
+ float cos = (float) Math.cos(thetaRad);
+ float sin = (float) Math.sin(thetaRad);
+
+ mP = new float[maxN + 1][];
+ mPDeriv = new float[maxN + 1][];
+ mP[0] = new float[] { 1.0f };
+ mPDeriv[0] = new float[] { 0.0f };
+ for (int n = 1; n <= maxN; n++) {
+ mP[n] = new float[n + 1];
+ mPDeriv[n] = new float[n + 1];
+ for (int m = 0; m <= n; m++) {
+ if (n == m) {
+ mP[n][m] = sin * mP[n - 1][m - 1];
+ mPDeriv[n][m] = cos * mP[n - 1][m - 1]
+ + sin * mPDeriv[n - 1][m - 1];
+ } else if (n == 1 || m == n - 1) {
+ mP[n][m] = cos * mP[n - 1][m];
+ mPDeriv[n][m] = -sin * mP[n - 1][m]
+ + cos * mPDeriv[n - 1][m];
+ } else {
+ assert n > 1 && m < n - 1;
+ float k = ((n - 1) * (n - 1) - m * m)
+ / (float) ((2 * n - 1) * (2 * n - 3));
+ mP[n][m] = cos * mP[n - 1][m] - k * mP[n - 2][m];
+ mPDeriv[n][m] = -sin * mP[n - 1][m]
+ + cos * mPDeriv[n - 1][m] - k * mPDeriv[n - 2][m];
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Compute the ration between the Gauss-normalized associated Legendre
+ * functions and the Schmidt quasi-normalized version. This is equivalent to
+ * sqrt((m==0?1:2)*(n-m)!/(n+m!))*(2n-1)!!/(n-m)!
+ */
+ private static float[][] computeSchmidtQuasiNormFactors(int maxN) {
+ float[][] schmidtQuasiNorm = new float[maxN + 1][];
+ schmidtQuasiNorm[0] = new float[] { 1.0f };
+ for (int n = 1; n <= maxN; n++) {
+ schmidtQuasiNorm[n] = new float[n + 1];
+ schmidtQuasiNorm[n][0] =
+ schmidtQuasiNorm[n - 1][0] * (2 * n - 1) / (float) n;
+ for (int m = 1; m <= n; m++) {
+ schmidtQuasiNorm[n][m] = schmidtQuasiNorm[n][m - 1]
+ * (float) Math.sqrt((n - m + 1) * (m == 1 ? 2 : 1)
+ / (float) (n + m));
+ }
+ }
+ return schmidtQuasiNorm;
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/ISensorService.aidl b/core/java/android/hardware/ISensorService.aidl
index 8aad9b465bda0..04af2aec45239 100644
--- a/core/java/android/hardware/ISensorService.aidl
+++ b/core/java/android/hardware/ISensorService.aidl
@@ -25,5 +25,5 @@ import android.os.ParcelFileDescriptor;
interface ISensorService
{
ParcelFileDescriptor getDataChanel();
- boolean enableSensor(IBinder listener, int sensor, int enable);
+ boolean enableSensor(IBinder listener, String name, int sensor, int enable);
}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index f02094eed63d1..3981f2784ef40 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -741,7 +741,7 @@ public class SensorManager extends IRotationWatcher.Stub
*/
@Deprecated
public void unregisterListener(SensorListener listener) {
- unregisterListener(listener, SENSOR_ALL);
+ unregisterListener(listener, SENSOR_ALL | SENSOR_ORIENTATION_RAW);
}
/**
@@ -829,9 +829,11 @@ public class SensorManager extends IRotationWatcher.Stub
}
}
+ String name = sensor.getName();
+ int handle = sensor.getHandle();
if (l == null) {
l = new ListenerDelegate(listener, sensor, handler);
- result = mSensorService.enableSensor(l, sensor.getHandle(), delay);
+ result = mSensorService.enableSensor(l, name, handle, delay);
if (result) {
sListeners.add(l);
sListeners.notify();
@@ -840,7 +842,7 @@ public class SensorManager extends IRotationWatcher.Stub
sSensorThread.startLocked(mSensorService);
}
} else {
- result = mSensorService.enableSensor(l, sensor.getHandle(), delay);
+ result = mSensorService.enableSensor(l, name, handle, delay);
if (result) {
l.addSensor(sensor);
}
@@ -861,8 +863,9 @@ public class SensorManager extends IRotationWatcher.Stub
ListenerDelegate l = sListeners.get(i);
if (l.getListener() == listener) {
// disable these sensors
+ String name = sensor.getName();
int handle = sensor.getHandle();
- mSensorService.enableSensor(l, handle, SENSOR_DISABLE);
+ mSensorService.enableSensor(l, name, handle, SENSOR_DISABLE);
// if we have no more sensors enabled on this listener,
// take it off the list.
if (l.removeSensor(sensor) == 0) {
@@ -886,7 +889,9 @@ public class SensorManager extends IRotationWatcher.Stub
if (l.getListener() == listener) {
// disable all sensors for this listener
for (Sensor sensor : l.getSensors()) {
- mSensorService.enableSensor(l, sensor.getHandle(), SENSOR_DISABLE);
+ String name = sensor.getName();
+ int handle = sensor.getHandle();
+ mSensorService.enableSensor(l, name, handle, SENSOR_DISABLE);
}
sListeners.remove(i);
break;
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 7d02f6588db4c..eedcc354e6133 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -24,6 +24,9 @@ import android.view.MotionEvent;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* AbstractInputMethodService provides a abstract base class for input methods.
* Normal input method implementations will not derive from this directly,
@@ -156,6 +159,13 @@ public abstract class AbstractInputMethodService extends Service
*/
public abstract AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
+ /**
+ * Implement this to handle {@link android.os.Binder#dump Binder.dump()}
+ * calls on your input method.
+ */
+ protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ }
+
@Override
final public IBinder onBind(Intent intent) {
if (mInputMethod == null) {
diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java
index e59f38b368db0..d9f10a96aac0f 100644
--- a/core/java/android/inputmethodservice/ExtractEditText.java
+++ b/core/java/android/inputmethodservice/ExtractEditText.java
@@ -2,6 +2,7 @@ package android.inputmethodservice;
import android.content.Context;
import android.util.AttributeSet;
+import android.view.inputmethod.ExtractedText;
import android.widget.EditText;
/***
@@ -9,6 +10,9 @@ import android.widget.EditText;
* extracted text in a full-screen input method.
*/
public class ExtractEditText extends EditText {
+ private InputMethodService mIME;
+ private int mSettingExtractedText;
+
public ExtractEditText(Context context) {
super(context, null);
}
@@ -20,4 +24,101 @@ public class ExtractEditText extends EditText {
public ExtractEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
+
+ void setIME(InputMethodService ime) {
+ mIME = ime;
+ }
+
+ /**
+ * Start making changes that will not be reported to the client. That
+ * is, {@link #onSelectionChanged(int, int)} will not result in sending
+ * the new selection to the client
+ */
+ public void startInternalChanges() {
+ mSettingExtractedText += 1;
+ }
+
+ /**
+ * Finish making changes that will not be reported to the client. That
+ * is, {@link #onSelectionChanged(int, int)} will not result in sending
+ * the new selection to the client
+ */
+ public void finishInternalChanges() {
+ mSettingExtractedText -= 1;
+ }
+
+ /**
+ * Implement just to keep track of when we are setting text from the
+ * client (vs. seeing changes in ourself from the user).
+ */
+ @Override public void setExtractedText(ExtractedText text) {
+ try {
+ mSettingExtractedText++;
+ super.setExtractedText(text);
+ } finally {
+ mSettingExtractedText--;
+ }
+ }
+
+ /**
+ * Report to the underlying text editor about selection changes.
+ */
+ @Override protected void onSelectionChanged(int selStart, int selEnd) {
+ if (mSettingExtractedText == 0 && mIME != null && selStart >= 0 && selEnd >= 0) {
+ mIME.onExtractedSelectionChanged(selStart, selEnd);
+ }
+ }
+
+ /**
+ * Redirect clicks to the IME for handling there. First allows any
+ * on click handler to run, though.
+ */
+ @Override public boolean performClick() {
+ if (!super.performClick() && mIME != null) {
+ mIME.onExtractedTextClicked();
+ return true;
+ }
+ return false;
+ }
+
+ @Override public boolean onTextContextMenuItem(int id) {
+ if (mIME != null) {
+ if (mIME.onExtractTextContextMenuItem(id)) {
+ return true;
+ }
+ }
+ return super.onTextContextMenuItem(id);
+ }
+
+ /**
+ * We are always considered to be an input method target.
+ */
+ public boolean isInputMethodTarget() {
+ return true;
+ }
+
+ /**
+ * Pretend like the window this view is in always has focus, so its
+ * highlight and cursor will be displayed.
+ */
+ @Override public boolean hasWindowFocus() {
+ return true;
+ }
+
+ /**
+ * Pretend like this view always has focus, so its
+ * highlight and cursor will be displayed.
+ */
+ @Override public boolean isFocused() {
+ return true;
+ }
+
+ /**
+ * Pretend like this view always has focus, so its
+ * highlight and cursor will be displayed.
+ */
+ @Override public boolean hasFocus() {
+ return true;
+ }
+
}
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 9abc23ba67032..a2c75b501e090 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -8,6 +8,8 @@ import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InputConnectionWrapper;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
@@ -18,6 +20,11 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/**
* Implements the internal IInputMethod interface to convert incoming calls
* on to it back to calls on the public InputMethod interface, scheduling
@@ -28,6 +35,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final String TAG = "InputMethodWrapper";
private static final boolean DEBUG = false;
+ private static final int DO_DUMP = 1;
private static final int DO_ATTACH_TOKEN = 10;
private static final int DO_SET_INPUT_CONTEXT = 20;
private static final int DO_UNSET_INPUT_CONTEXT = 30;
@@ -39,9 +47,14 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_SHOW_SOFT_INPUT = 60;
private static final int DO_HIDE_SOFT_INPUT = 70;
+ final AbstractInputMethodService mTarget;
final HandlerCaller mCaller;
final InputMethod mInputMethod;
+ static class Notifier {
+ boolean notified;
+ }
+
// NOTE: we should have a cache of these.
static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
final Context mContext;
@@ -64,7 +77,9 @@ class IInputMethodWrapper extends IInputMethod.Stub
}
}
- public IInputMethodWrapper(Context context, InputMethod inputMethod) {
+ public IInputMethodWrapper(AbstractInputMethodService context,
+ InputMethod inputMethod) {
+ mTarget = context;
mCaller = new HandlerCaller(context, this);
mInputMethod = inputMethod;
}
@@ -75,6 +90,20 @@ class IInputMethodWrapper extends IInputMethod.Stub
public void executeMessage(Message msg) {
switch (msg.what) {
+ case DO_DUMP: {
+ HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
+ try {
+ mTarget.dump((FileDescriptor)args.arg1,
+ (PrintWriter)args.arg2, (String[])args.arg3);
+ } catch (RuntimeException e) {
+ ((PrintWriter)args.arg2).println("Exception: " + e);
+ }
+ synchronized (args.arg4) {
+ ((CountDownLatch)args.arg4).countDown();
+ }
+ return;
+ }
+
case DO_ATTACH_TOKEN: {
mInputMethod.attachToken((IBinder)msg.obj);
return;
@@ -86,12 +115,22 @@ class IInputMethodWrapper extends IInputMethod.Stub
case DO_UNSET_INPUT_CONTEXT:
mInputMethod.unbindInput();
return;
- case DO_START_INPUT:
- mInputMethod.startInput((EditorInfo)msg.obj);
+ case DO_START_INPUT: {
+ HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
+ IInputContext inputContext = (IInputContext)args.arg1;
+ InputConnection ic = inputContext != null
+ ? new InputConnectionWrapper(inputContext) : null;
+ mInputMethod.startInput(ic, (EditorInfo)args.arg2);
return;
- case DO_RESTART_INPUT:
- mInputMethod.restartInput((EditorInfo)msg.obj);
+ }
+ case DO_RESTART_INPUT: {
+ HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
+ IInputContext inputContext = (IInputContext)args.arg1;
+ InputConnection ic = inputContext != null
+ ? new InputConnectionWrapper(inputContext) : null;
+ mInputMethod.restartInput(ic, (EditorInfo)args.arg2);
return;
+ }
case DO_CREATE_SESSION: {
mInputMethod.createSession(new InputMethodSessionCallbackWrapper(
mCaller.mContext, (IInputMethodCallback)msg.obj));
@@ -105,8 +144,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
mInputMethod.revokeSession((InputMethodSession)msg.obj);
return;
case DO_SHOW_SOFT_INPUT:
- mInputMethod.showSoftInput(
- msg.arg1 != 0 ? InputMethod.SHOW_EXPLICIT : 0);
+ mInputMethod.showSoftInput(msg.arg1);
return;
case DO_HIDE_SOFT_INPUT:
mInputMethod.hideSoftInput();
@@ -115,6 +153,28 @@ class IInputMethodWrapper extends IInputMethod.Stub
Log.w(TAG, "Unhandled message code: " + msg.what);
}
+ @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ if (mTarget.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+
+ fout.println("Permission Denial: can't dump InputMethodManager from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ CountDownLatch latch = new CountDownLatch(1);
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOOOO(DO_DUMP,
+ fd, fout, args, latch));
+ try {
+ if (!latch.await(5, TimeUnit.SECONDS)) {
+ fout.println("Timeout waiting for dump");
+ }
+ } catch (InterruptedException e) {
+ fout.println("Interrupted waiting for dump");
+ }
+ }
+
public void attachToken(IBinder token) {
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_ATTACH_TOKEN, token));
}
@@ -130,12 +190,14 @@ class IInputMethodWrapper extends IInputMethod.Stub
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT));
}
- public void startInput(EditorInfo attribute) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_START_INPUT, attribute));
+ public void startInput(IInputContext inputContext, EditorInfo attribute) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_INPUT,
+ inputContext, attribute));
}
- public void restartInput(EditorInfo attribute) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RESTART_INPUT, attribute));
+ public void restartInput(IInputContext inputContext, EditorInfo attribute) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_RESTART_INPUT,
+ inputContext, attribute));
}
public void createSession(IInputMethodCallback callback) {
@@ -163,9 +225,9 @@ class IInputMethodWrapper extends IInputMethod.Stub
}
}
- public void showSoftInput(boolean explicit) {
+ public void showSoftInput(int flags) {
mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_SHOW_SOFT_INPUT,
- explicit ? 1 : 0));
+ flags));
}
public void hideSoftInput() {
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 3a9b26a9c6f54..ea5f7414d63de 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -26,7 +26,13 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.IBinder;
+import android.provider.Settings;
+import android.text.Layout;
+import android.text.Spannable;
+import android.text.method.MovementMethod;
import android.util.Log;
+import android.util.PrintWriterPrinter;
+import android.util.Printer;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -45,12 +51,31 @@ import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.EditorInfo;
import android.widget.FrameLayout;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* InputMethodService provides a standard implementation of an InputMethod,
* which final implementations can derive from and customize. See the
* base class {@link AbstractInputMethodService} and the {@link InputMethod}
* interface for more information on the basics of writing input methods.
*
+ * In addition to the normal Service lifecycle methods, this class
+ * introduces some new specific callbacks that most subclasses will want
+ * to make use of: An input method has significant discretion in how it goes about its
* work: the {@link android.inputmethodservice.InputMethodService} provides
* a basic framework for standard UI elements (input view, candidates view,
@@ -184,6 +209,7 @@ public class InputMethodService extends AbstractInputMethodService {
LayoutInflater mInflater;
View mRootView;
SoftInputWindow mWindow;
+ boolean mInitialized;
boolean mWindowCreated;
boolean mWindowAdded;
boolean mWindowVisible;
@@ -196,11 +222,16 @@ public class InputMethodService extends AbstractInputMethodService {
InputBinding mInputBinding;
InputConnection mInputConnection;
boolean mInputStarted;
+ boolean mInputViewStarted;
+ boolean mCandidatesViewStarted;
+ InputConnection mStartedInputConnection;
EditorInfo mInputEditorInfo;
boolean mShowInputRequested;
boolean mLastShowInputRequested;
- boolean mShowCandidatesRequested;
+ int mCandidatesVisibility;
+
+ boolean mShowInputForced;
boolean mFullscreenApplied;
boolean mIsFullscreen;
@@ -262,6 +293,7 @@ public class InputMethodService extends AbstractInputMethodService {
mInputConnection = binding.getConnection();
if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
+ " ic=" + mInputConnection);
+ initialize();
onBindInput();
}
@@ -277,14 +309,14 @@ public class InputMethodService extends AbstractInputMethodService {
mInputConnection = null;
}
- public void startInput(EditorInfo attribute) {
+ public void startInput(InputConnection ic, EditorInfo attribute) {
if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute);
- doStartInput(attribute, false);
+ doStartInput(ic, attribute, false);
}
- public void restartInput(EditorInfo attribute) {
+ public void restartInput(InputConnection ic, EditorInfo attribute) {
if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute);
- doStartInput(attribute, true);
+ doStartInput(ic, attribute, true);
}
/**
@@ -293,6 +325,7 @@ public class InputMethodService extends AbstractInputMethodService {
public void hideSoftInput() {
if (DEBUG) Log.v(TAG, "hideSoftInput()");
mShowInputRequested = false;
+ mShowInputForced = false;
hideWindow();
}
@@ -316,8 +349,7 @@ public class InputMethodService extends AbstractInputMethodService {
return;
}
if (DEBUG) Log.v(TAG, "finishInput() in " + this);
- onFinishInput();
- mInputStarted = false;
+ doFinishInput();
}
/**
@@ -444,17 +476,37 @@ public class InputMethodService extends AbstractInputMethodService {
mWindow.getWindow().setLayout(FILL_PARENT, WRAP_CONTENT);
}
+ /**
+ * This is a hook that subclasses can use to perform initialization of
+ * their interface. It is called for you prior to any of your UI objects
+ * being created, both after the service is first created and after a
+ * configuration change happens.
+ */
+ public void onInitializeInterface() {
+ }
+
+ void initialize() {
+ if (!mInitialized) {
+ mInitialized = true;
+ onInitializeInterface();
+ }
+ }
+
void initViews() {
- mWindowVisible = false;
+ mInitialized = false;
mWindowCreated = false;
mShowInputRequested = false;
- mShowCandidatesRequested = false;
+ mShowInputForced = false;
mRootView = mInflater.inflate(
com.android.internal.R.layout.input_method, null);
mWindow.setContentView(mRootView);
mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
-
+ if (Settings.System.getInt(getContentResolver(),
+ Settings.System.FANCY_IME_ANIMATIONS, 0) != 0) {
+ mWindow.getWindow().setWindowAnimations(
+ com.android.internal.R.style.Animation_InputMethodFancy);
+ }
mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea);
mExtractView = null;
mExtractEditText = null;
@@ -466,7 +518,8 @@ public class InputMethodService extends AbstractInputMethodService {
mIsInputViewShown = false;
mExtractFrame.setVisibility(View.GONE);
- mCandidatesFrame.setVisibility(View.INVISIBLE);
+ mCandidatesVisibility = getCandidatesHiddenVisibility();
+ mCandidatesFrame.setVisibility(mCandidatesVisibility);
mInputFrame.setVisibility(View.GONE);
}
@@ -486,19 +539,36 @@ public class InputMethodService extends AbstractInputMethodService {
* regenerating the input method UI as a result of the configuration
* change, so you can rely on your {@link #onCreateInputView} and
* other methods being called as appropriate due to a configuration change.
+ *
+ * When a configuration change does happen,
+ * {@link #onInitializeInterface()} is guaranteed to be called the next
+ * time prior to any of the other input or UI creation callbacks. The
+ * following will be called immediately depending if appropriate for current
+ * state: {@link #onStartInput} if input is active, and
+ * {@link #onCreateInputView} and {@link #onStartInputView} and related
+ * appropriate functions if the UI is displayed.
*/
@Override public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
boolean visible = mWindowVisible;
boolean showingInput = mShowInputRequested;
- boolean showingCandidates = mShowCandidatesRequested;
+ boolean showingForced = mShowInputForced;
+ boolean showingCandidates = mCandidatesVisibility == View.VISIBLE;
initViews();
+ mInputViewStarted = false;
+ mCandidatesViewStarted = false;
+ if (mInputStarted) {
+ doStartInput(getCurrentInputConnection(),
+ getCurrentInputEditorInfo(), true);
+ }
if (visible) {
- if (showingCandidates) {
- setCandidatesViewShown(true);
- }
- if (showingInput) {
+ if (showingForced) {
+ // If we are showing the full soft keyboard, then go through
+ // this path to take care of current decisions about fullscreen
+ // etc.
+ onShowRequested(InputMethod.SHOW_FORCED|InputMethod.SHOW_EXPLICIT);
+ } else if (showingInput) {
// If we are showing the full soft keyboard, then go through
// this path to take care of current decisions about fullscreen
// etc.
@@ -507,6 +577,9 @@ public class InputMethodService extends AbstractInputMethodService {
// Otherwise just put it back for its candidates.
showWindow(false);
}
+ if (showingCandidates) {
+ setCandidatesViewShown(true);
+ }
}
}
@@ -568,6 +641,10 @@ public class InputMethodService extends AbstractInputMethodService {
* the input method, or null if there is none.
*/
public InputConnection getCurrentInputConnection() {
+ InputConnection ic = mStartedInputConnection;
+ if (ic != null) {
+ return ic;
+ }
return mInputConnection;
}
@@ -594,6 +671,7 @@ public class InputMethodService extends AbstractInputMethodService {
changed = true;
mIsFullscreen = isFullscreen;
mFullscreenApplied = true;
+ initialize();
Drawable bg = onCreateBackgroundDrawable();
if (bg == null) {
// We need to give the window a real drawable, so that it
@@ -708,6 +786,7 @@ public class InputMethodService extends AbstractInputMethodService {
mIsInputViewShown = isShown;
mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
if (mInputView == null) {
+ initialize();
View v = onCreateInputView();
if (v != null) {
setInputView(v);
@@ -716,13 +795,20 @@ public class InputMethodService extends AbstractInputMethodService {
}
}
+ /**
+ * Returns true if we have been asked to show our input view.
+ */
+ public boolean isShowInputRequested() {
+ return mShowInputRequested;
+ }
+
/**
* Return whether the soft input view is currently shown to the
* user. This is the state that was last determined and
* applied by {@link #updateInputViewShown()}.
*/
public boolean isInputViewShown() {
- return mIsInputViewShown;
+ return mIsInputViewShown && mWindowVisible;
}
/**
@@ -744,9 +830,10 @@ public class InputMethodService extends AbstractInputMethodService {
* it is hidden.
*/
public void setCandidatesViewShown(boolean shown) {
- if (mShowCandidatesRequested != shown) {
- mCandidatesFrame.setVisibility(shown ? View.VISIBLE : View.INVISIBLE);
- mShowCandidatesRequested = shown;
+ int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility();
+ if (mCandidatesVisibility != vis) {
+ mCandidatesFrame.setVisibility(vis);
+ mCandidatesVisibility = vis;
}
if (!mShowInputRequested && mWindowVisible != shown) {
// If we are being asked to show the candidates view while the app
@@ -760,10 +847,24 @@ public class InputMethodService extends AbstractInputMethodService {
}
}
+ /**
+ * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE}
+ * or {@link View#GONE View.GONE}) of the candidates view when it is not
+ * shown. The default implementation returns GONE when in fullscreen mode,
+ * otherwise VISIBLE. Be careful if you change this to return GONE in
+ * other situations -- if showing or hiding the candidates view causes
+ * your window to resize, this can cause temporary drawing artifacts as
+ * the resize takes place.
+ */
+ public int getCandidatesHiddenVisibility() {
+ return isFullscreenMode() ? View.GONE : View.INVISIBLE;
+ }
+
public void setStatusIcon(int iconResId) {
mStatusIcon = iconResId;
- if (mInputConnection != null && mWindowVisible) {
- mInputConnection.showStatusIcon(getPackageName(), iconResId);
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null && mWindowVisible) {
+ ic.showStatusIcon(getPackageName(), iconResId);
}
}
@@ -783,11 +884,12 @@ public class InputMethodService extends AbstractInputMethodService {
mExtractFrame.removeAllViews();
mExtractFrame.addView(view, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.FILL_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT));
+ ViewGroup.LayoutParams.FILL_PARENT));
mExtractView = view;
if (view != null) {
mExtractEditText = (ExtractEditText)view.findViewById(
com.android.internal.R.id.inputExtractEditText);
+ mExtractEditText.setIME(this);
startExtractingText();
} else {
mExtractEditText = null;
@@ -889,6 +991,72 @@ public class InputMethodService extends AbstractInputMethodService {
public void onStartInputView(EditorInfo info, boolean restarting) {
}
+ /**
+ * Called when the input view is being hidden from the user. This will
+ * be called either prior to hiding the window, or prior to switching to
+ * another target for editing.
+ *
+ * The default
+ * implementation uses the InputConnection to clear any active composing
+ * text; you can override this (not calling the base class implementation)
+ * to perform whatever behavior you would like.
+ *
+ * @boolean finishingInput If true, {@link #onFinishInput} will be
+ * called immediately after.
+ */
+ public void onFinishInputView(boolean finishingInput) {
+ if (!finishingInput) {
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.finishComposingText();
+ }
+ }
+ }
+
+ /**
+ * Called when only the candidates view has been shown for showing
+ * processing as the user enters text through a hard keyboard.
+ * This will always be called after {@link #onStartInput},
+ * allowing you to do your general setup there and just view-specific
+ * setup here. You are guaranteed that {@link #onCreateCandidatesView()}
+ * will have been called some time before this function is called.
+ *
+ * Note that this will not be called when the input method
+ * is running in full editing mode, and thus receiving
+ * {@link #onStartInputView} to initiate that operation. This is only
+ * for the case when candidates are being shown while the input method
+ * editor is hidden but wants to show its candidates UI as text is
+ * entered through some other mechanism.
+ *
+ * @param info Description of the type of text being edited.
+ * @param restarting Set to true if we are restarting input on the
+ * same text field as before.
+ */
+ public void onStartCandidatesView(EditorInfo info, boolean restarting) {
+ }
+
+ /**
+ * Called when the candidates view is being hidden from the user. This will
+ * be called either prior to hiding the window, or prior to switching to
+ * another target for editing.
+ *
+ * The default
+ * implementation uses the InputConnection to clear any active composing
+ * text; you can override this (not calling the base class implementation)
+ * to perform whatever behavior you would like.
+ *
+ * @boolean finishingInput If true, {@link #onFinishInput} will be
+ * called immediately after.
+ */
+ public void onFinishCandidatesView(boolean finishingInput) {
+ if (!finishingInput) {
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.finishComposingText();
+ }
+ }
+ }
+
/**
* The system has decided that it may be time to show your input method.
* This is called due to a corresponding call to your
@@ -905,10 +1073,22 @@ public class InputMethodService extends AbstractInputMethodService {
if (!onEvaluateInputViewShown()) {
return;
}
- if ((flags&InputMethod.SHOW_EXPLICIT) == 0 && onEvaluateFullscreenMode()) {
- // Don't show if this is not explicit requested by the user and
- // the input method is fullscreen. That would be too disruptive.
- return;
+ if ((flags&InputMethod.SHOW_EXPLICIT) == 0) {
+ if (onEvaluateFullscreenMode()) {
+ // Don't show if this is not explicitly requested by the user and
+ // the input method is fullscreen. That would be too disruptive.
+ return;
+ }
+ Configuration config = getResources().getConfiguration();
+ if (config.keyboard != Configuration.KEYBOARD_NOKEYS) {
+ // And if the device has a hard keyboard, even if it is
+ // currently hidden, don't show the input method implicitly.
+ // These kinds of devices don't need it that much.
+ return;
+ }
+ }
+ if ((flags&InputMethod.SHOW_FORCED) != 0) {
+ mShowInputForced = true;
}
showWindow(true);
}
@@ -922,6 +1102,7 @@ public class InputMethodService extends AbstractInputMethodService {
+ " mInputStarted=" + mInputStarted);
boolean doShowInput = false;
boolean wasVisible = mWindowVisible;
+ boolean wasCreated = mWindowCreated;
mWindowVisible = true;
if (!mShowInputRequested) {
if (mInputStarted) {
@@ -935,41 +1116,66 @@ public class InputMethodService extends AbstractInputMethodService {
}
if (DEBUG) Log.v(TAG, "showWindow: updating UI");
+ initialize();
updateFullscreenMode();
updateInputViewShown();
if (!mWindowAdded || !mWindowCreated) {
mWindowAdded = true;
mWindowCreated = true;
+ initialize();
+ if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView");
View v = onCreateCandidatesView();
if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v);
if (v != null) {
setCandidatesView(v);
}
}
- if (doShowInput) {
- if (mInputStarted) {
- if (DEBUG) Log.v(TAG, "showWindow: starting input view");
+ if (mShowInputRequested) {
+ if (!mInputViewStarted) {
+ if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
+ mInputViewStarted = true;
onStartInputView(mInputEditorInfo, false);
}
+ } else if (!mCandidatesViewStarted) {
+ if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
+ mCandidatesViewStarted = true;
+ onStartCandidatesView(mInputEditorInfo, false);
+ }
+
+ if (doShowInput) {
startExtractingText();
}
if (!wasVisible) {
if (DEBUG) Log.v(TAG, "showWindow: showing!");
mWindow.show();
- if (mInputConnection != null) {
- mInputConnection.showStatusIcon(getPackageName(), mStatusIcon);
+ }
+
+ if (!wasVisible || !wasCreated) {
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.showStatusIcon(getPackageName(), mStatusIcon);
}
}
}
public void hideWindow() {
+ if (mInputViewStarted) {
+ if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
+ onFinishInputView(false);
+ } else if (mCandidatesViewStarted) {
+ if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
+ onFinishCandidatesView(false);
+ }
+ mInputViewStarted = false;
+ mCandidatesViewStarted = false;
if (mWindowVisible) {
mWindow.hide();
mWindowVisible = false;
- if (mInputConnection != null) {
- mInputConnection.hideStatusIcon();
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.hideStatusIcon();
}
}
}
@@ -1008,18 +1214,45 @@ public class InputMethodService extends AbstractInputMethodService {
public void onStartInput(EditorInfo attribute, boolean restarting) {
}
- void doStartInput(EditorInfo attribute, boolean restarting) {
- if (mInputStarted && !restarting) {
+ void doFinishInput() {
+ if (mInputViewStarted) {
+ if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
+ onFinishInputView(true);
+ } else if (mCandidatesViewStarted) {
+ if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
+ onFinishCandidatesView(true);
+ }
+ mInputViewStarted = false;
+ mCandidatesViewStarted = false;
+ if (mInputStarted) {
+ if (DEBUG) Log.v(TAG, "CALL: onFinishInput");
onFinishInput();
}
+ mInputStarted = false;
+ mStartedInputConnection = null;
+ }
+
+ void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
+ if (!restarting) {
+ doFinishInput();
+ }
mInputStarted = true;
+ mStartedInputConnection = ic;
mInputEditorInfo = attribute;
+ initialize();
+ if (DEBUG) Log.v(TAG, "CALL: onStartInput");
onStartInput(attribute, restarting);
if (mWindowVisible) {
if (mShowInputRequested) {
+ if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
+ mInputViewStarted = true;
onStartInputView(mInputEditorInfo, restarting);
+ startExtractingText();
+ } else if (mCandidatesVisibility == View.VISIBLE) {
+ if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
+ mCandidatesViewStarted = true;
+ onStartCandidatesView(mInputEditorInfo, restarting);
}
- startExtractingText();
}
}
@@ -1029,8 +1262,17 @@ public class InputMethodService extends AbstractInputMethodService {
* {@link #onStartInput(EditorInfo, boolean)} to perform input in a
* new editor, or the input method may be left idle. This method is
* not called when input restarts in the same editor.
+ *
+ * The default
+ * implementation uses the InputConnection to clear any active composing
+ * text; you can override this (not calling the base class implementation)
+ * to perform whatever behavior you would like.
*/
public void onFinishInput() {
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.finishComposingText();
+ }
}
/**
@@ -1073,9 +1315,12 @@ public class InputMethodService extends AbstractInputMethodService {
public void onUpdateSelection(int oldSelStart, int oldSelEnd,
int newSelStart, int newSelEnd,
int candidatesStart, int candidatesEnd) {
- if (mExtractEditText != null && mExtractedText != null) {
+ final ExtractEditText eet = mExtractEditText;
+ if (eet != null && mExtractedText != null) {
final int off = mExtractedText.startOffset;
- mExtractEditText.setSelection(newSelStart-off, newSelEnd-off);
+ eet.startInternalChanges();
+ eet.setSelection(newSelStart-off, newSelEnd-off);
+ eet.finishInternalChanges();
}
}
@@ -1100,6 +1345,18 @@ public class InputMethodService extends AbstractInputMethodService {
.hideSoftInputFromInputMethod(mToken, flags);
}
+ /**
+ * Override this to intercept key down events before they are processed by the
+ * application. If you return true, the application will not itself
+ * process the event. If you return true, the normal application processing
+ * will occur as if the IME had not seen the event at all.
+ *
+ * The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
+ * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In
+ * additional, in fullscreen mode only, it will consume DPAD movement
+ * events to move the cursor in the extracted text view, not allowing
+ * them to perform navigation in the underlying application.
+ */
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
&& event.getRepeatCount() == 0) {
@@ -1108,25 +1365,54 @@ public class InputMethodService extends AbstractInputMethodService {
// consume the back key.
dismissSoftInput(0);
return true;
- }
- if (mShowCandidatesRequested) {
- // If the candidates are shown, we just want to make sure
- // they are now hidden but otherwise let the app execute
- // the back.
- // XXX this needs better interaction with the soft input
- // implementation.
- //setCandidatesViewShown(false);
+ } else if (mWindowVisible) {
+ if (mCandidatesVisibility == View.VISIBLE) {
+ // If we are showing candidates even if no input area, then
+ // hide them.
+ setCandidatesViewShown(false);
+ return true;
+ } else {
+ // If we have the window visible for some other reason --
+ // most likely to show candidates -- then just get rid
+ // of it. This really shouldn't happen, but just in case...
+ hideWindow();
+ return true;
+ }
}
}
- return false;
+
+ return doMovementKey(keyCode, event, MOVEMENT_DOWN);
}
+ /**
+ * Override this to intercept special key multiple events before they are
+ * processed by the
+ * application. If you return true, the application will not itself
+ * process the event. If you return true, the normal application processing
+ * will occur as if the IME had not seen the event at all.
+ *
+ * The default implementation always returns false, except when
+ * in fullscreen mode, where it will consume DPAD movement
+ * events to move the cursor in the extracted text view, not allowing
+ * them to perform navigation in the underlying application.
+ */
public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
- return false;
+ return doMovementKey(keyCode, event, count);
}
+ /**
+ * Override this to intercept key up events before they are processed by the
+ * application. If you return true, the application will not itself
+ * process the event. If you return true, the normal application processing
+ * will occur as if the IME had not seen the event at all.
+ *
+ * The default implementation always returns false, except when
+ * in fullscreen mode, where it will consume DPAD movement
+ * events to move the cursor in the extracted text view, not allowing
+ * them to perform navigation in the underlying application.
+ */
public boolean onKeyUp(int keyCode, KeyEvent event) {
- return false;
+ return doMovementKey(keyCode, event, MOVEMENT_UP);
}
public boolean onTrackballEvent(MotionEvent event) {
@@ -1136,21 +1422,176 @@ public class InputMethodService extends AbstractInputMethodService {
public void onAppPrivateCommand(String action, Bundle data) {
}
+ static final int MOVEMENT_DOWN = -1;
+ static final int MOVEMENT_UP = -2;
+
+ boolean doMovementKey(int keyCode, KeyEvent event, int count) {
+ final ExtractEditText eet = mExtractEditText;
+ if (isFullscreenMode() && isInputViewShown() && eet != null) {
+ // If we are in fullscreen mode, the cursor will move around
+ // the extract edit text, but should NOT cause focus to move
+ // to other fields.
+ MovementMethod movement = eet.getMovementMethod();
+ Layout layout = eet.getLayout();
+ if (movement != null && layout != null) {
+ // We want our own movement method to handle the key, so the
+ // cursor will properly move in our own word wrapping.
+ if (count == MOVEMENT_DOWN) {
+ if (movement.onKeyDown(eet,
+ (Spannable)eet.getText(), keyCode, event)) {
+ return true;
+ }
+ } else if (count == MOVEMENT_UP) {
+ if (movement.onKeyUp(eet,
+ (Spannable)eet.getText(), keyCode, event)) {
+ return true;
+ }
+ } else {
+ KeyEvent down = new KeyEvent(event, KeyEvent.ACTION_DOWN);
+ if (movement.onKeyDown(eet,
+ (Spannable)eet.getText(), keyCode, down)) {
+ KeyEvent up = new KeyEvent(event, KeyEvent.ACTION_UP);
+ movement.onKeyUp(eet,
+ (Spannable)eet.getText(), keyCode, up);
+ while (--count > 0) {
+ movement.onKeyDown(eet,
+ (Spannable)eet.getText(), keyCode, down);
+ movement.onKeyUp(eet,
+ (Spannable)eet.getText(), keyCode, up);
+ }
+ }
+ }
+ }
+ // Regardless of whether the movement method handled the key,
+ // we never allow DPAD navigation to the application.
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ case KeyEvent.KEYCODE_DPAD_UP:
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * This is called when the user has moved the cursor in the extracted
+ * text view, when running in fullsreen mode. The default implementation
+ * performs the corresponding selection change on the underlying text
+ * editor.
+ */
+ public void onExtractedSelectionChanged(int start, int end) {
+ InputConnection conn = getCurrentInputConnection();
+ if (conn != null) {
+ conn.setSelection(start, end);
+ }
+ }
+
+ /**
+ * This is called when the user has clicked on the extracted text view,
+ * when running in fullscreen mode. The default implementation hides
+ * the candidates view when this happens. Re-implement this to provide
+ * whatever behavior you want.
+ */
+ public void onExtractedTextClicked() {
+ setCandidatesViewShown(false);
+ }
+
+ /**
+ * This is called when the user has selected a context menu item from the
+ * extracted text view, when running in fullscreen mode. The default
+ * implementation sends this action to the current InputConnection's
+ * {@link InputConnection#performContextMenuAction(int)}, for it
+ * to be processed in underlying "real" editor. Re-implement this to
+ * provide whatever behavior you want.
+ */
+ public boolean onExtractTextContextMenuItem(int id) {
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.performContextMenuAction(id);
+ }
+ return true;
+ }
+
void startExtractingText() {
- if (mExtractEditText != null && getCurrentInputStarted()
+ final ExtractEditText eet = mExtractEditText;
+ if (eet != null && getCurrentInputStarted()
&& isFullscreenMode()) {
mExtractedToken++;
ExtractedTextRequest req = new ExtractedTextRequest();
req.token = mExtractedToken;
+ req.flags = InputConnection.GET_TEXT_WITH_STYLES;
req.hintMaxLines = 10;
req.hintMaxChars = 10000;
- mExtractedText = mInputConnection.getExtractedText(req,
- InputConnection.EXTRACTED_TEXT_MONITOR);
- if (mExtractedText != null) {
- mExtractEditText.setExtractedText(mExtractedText);
+ mExtractedText = getCurrentInputConnection().getExtractedText(req,
+ InputConnection.GET_EXTRACTED_TEXT_MONITOR);
+ try {
+ eet.startInternalChanges();
+ int inputType = getCurrentInputEditorInfo().inputType;
+ if ((inputType&EditorInfo.TYPE_MASK_CLASS)
+ == EditorInfo.TYPE_CLASS_TEXT) {
+ if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) {
+ inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
+ }
+ }
+ eet.setInputType(inputType);
+ eet.setHint(mInputEditorInfo.hintText);
+ if (mExtractedText != null) {
+ eet.setExtractedText(mExtractedText);
+ }
+ } finally {
+ eet.finishInternalChanges();
}
- mExtractEditText.setInputType(getCurrentInputEditorInfo().inputType);
- mExtractEditText.setHint(mInputEditorInfo.hintText);
}
}
+
+ /**
+ * Performs a dump of the InputMethodService's internal state. Override
+ * to add your own information to the dump.
+ */
+ @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ final Printer p = new PrintWriterPrinter(fout);
+ p.println("Input method service state for " + this + ":");
+ p.println(" mWindowCreated=" + mWindowCreated
+ + " mWindowAdded=" + mWindowAdded
+ + " mWindowVisible=" + mWindowVisible);
+ p.println(" Configuration=" + getResources().getConfiguration());
+ p.println(" mToken=" + mToken);
+ p.println(" mInputBinding=" + mInputBinding);
+ p.println(" mInputConnection=" + mInputConnection);
+ p.println(" mStartedInputConnection=" + mStartedInputConnection);
+ p.println(" mInputStarted=" + mInputStarted
+ + " mInputViewStarted=" + mInputViewStarted
+ + " mCandidatesViewStarted=" + mCandidatesViewStarted);
+
+ if (mInputEditorInfo != null) {
+ p.println(" mInputEditorInfo:");
+ mInputEditorInfo.dump(p, " ");
+ } else {
+ p.println(" mInputEditorInfo: null");
+ }
+
+ p.println(" mShowInputRequested=" + mShowInputRequested
+ + " mLastShowInputRequested=" + mLastShowInputRequested
+ + " mShowInputForced=" + mShowInputForced);
+ p.println(" mCandidatesVisibility=" + mCandidatesVisibility
+ + " mFullscreenApplied=" + mFullscreenApplied
+ + " mIsFullscreen=" + mIsFullscreen);
+
+ if (mExtractedText != null) {
+ p.println(" mExtractedText:");
+ p.println(" text=" + mExtractedText.text.length() + " chars"
+ + " startOffset=" + mExtractedText.startOffset);
+ p.println(" selectionStart=" + mExtractedText.selectionStart
+ + " selectionEnd=" + mExtractedText.selectionEnd
+ + " flags=0x" + Integer.toHexString(mExtractedText.flags));
+ } else {
+ p.println(" mExtractedText: null");
+ }
+ p.println(" mExtractedToken=" + mExtractedToken);
+ p.println(" mIsInputViewShown=" + mIsInputViewShown
+ + " mStatusIcon=" + mStatusIcon);
+ }
}
diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java
index cfd3188bec4df..228acbee97e0f 100755
--- a/core/java/android/inputmethodservice/Keyboard.java
+++ b/core/java/android/inputmethodservice/Keyboard.java
@@ -177,13 +177,13 @@ public class Keyboard {
parent.mDisplayWidth, parent.mDefaultWidth);
defaultHeight = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_keyHeight,
- parent.mDisplayWidth, parent.mDefaultHeight);
- defaultHorizontalGap = getDimensionOrFraction(a,
+ parent.mDisplayHeight, parent.mDefaultHeight);
+ defaultHorizontalGap = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_horizontalGap,
parent.mDisplayWidth, parent.mDefaultHorizontalGap);
verticalGap = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_verticalGap,
- parent.mDisplayWidth, parent.mDefaultVerticalGap);
+ parent.mDisplayHeight, parent.mDefaultVerticalGap);
a.recycle();
a = res.obtainAttributes(Xml.asAttributeSet(parser),
com.android.internal.R.styleable.Keyboard_Row);
@@ -540,7 +540,6 @@ public class Keyboard {
row.defaultHorizontalGap = mDefaultHorizontalGap;
row.verticalGap = mDefaultVerticalGap;
row.rowEdgeFlags = EDGE_TOP | EDGE_BOTTOM;
-
final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns;
for (int i = 0; i < characters.length(); i++) {
char c = characters.charAt(i);
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 2f3b54bee7161..b2c74f24bf00e 100755
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -149,6 +149,7 @@ public class KeyboardView extends View implements View.OnClickListener {
private int mMiniKeyboardOffsetY;
private Map
+ * If an application uses the network in the background, it should listen
+ * for this broadcast and stop using the background data if the value is
+ * false.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED =
+ "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
+
public static final int TYPE_MOBILE = 0;
public static final int TYPE_WIFI = 1;
@@ -223,6 +237,43 @@ public class ConnectivityManager
}
}
+ /**
+ * Returns the value of the setting for background data usage. If false,
+ * applications should not use the network if the application is not in the
+ * foreground. Developers should respect this setting, and check the value
+ * of this before performing any background data operations.
+ *
+ * All applications that have background services that use the network
+ * should listen to {@link #ACTION_BACKGROUND_DATA_SETTING_CHANGED}.
+ *
+ * @return Whether background data usage is allowed.
+ */
+ public boolean getBackgroundDataSetting() {
+ try {
+ return mService.getBackgroundDataSetting();
+ } catch (RemoteException e) {
+ // Err on the side of safety
+ return false;
+ }
+ }
+
+ /**
+ * Sets the value of the setting for background data usage.
+ *
+ * @param allowBackgroundData Whether an application should use data while
+ * it is in the background.
+ *
+ * @attr ref android.Manifest.permission#CHANGE_BACKGROUND_DATA_SETTING
+ * @see #getBackgroundDataSetting()
+ * @hide
+ */
+ public void setBackgroundDataSetting(boolean allowBackgroundData) {
+ try {
+ mService.setBackgroundDataSetting(allowBackgroundData);
+ } catch (RemoteException e) {
+ }
+ }
+
/**
* Don't allow use of default constructor.
*/
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index e1d921f4d9a0c..de6859809d82e 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -44,4 +44,8 @@ interface IConnectivityManager
int stopUsingNetworkFeature(int networkType, in String feature);
boolean requestRouteToHost(int networkType, int hostAddress);
+
+ boolean getBackgroundDataSetting();
+
+ void setBackgroundDataSetting(boolean allowBackgroundData);
}
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 32a26e42d6157..c23df217a856d 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -2235,12 +2235,13 @@ public abstract class Uri implements Parcelable, Comparable Most developers will not implement this class directly, instead using the
- * aidl tool to describe the desired
+ * aidl tool to describe the desired
* interface, having it generate the appropriate Binder subclass. You can,
* however, derive directly from Binder to implement your own custom RPC
* protocol or simply instantiate a raw Binder object directly to use as a
@@ -194,18 +194,15 @@ public class Binder implements IBinder {
return true;
} else if (code == DUMP_TRANSACTION) {
ParcelFileDescriptor fd = data.readFileDescriptor();
- FileOutputStream fout = fd != null
- ? new FileOutputStream(fd.getFileDescriptor()) : null;
- PrintWriter pw = fout != null ? new PrintWriter(fout) : null;
- if (pw != null) {
- String[] args = data.readStringArray();
- dump(fd.getFileDescriptor(), pw, args);
- pw.flush();
- }
+ String[] args = data.readStringArray();
if (fd != null) {
try {
- fd.close();
- } catch (IOException e) {
+ dump(fd.getFileDescriptor(), args);
+ } finally {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ }
}
}
return true;
@@ -213,6 +210,20 @@ public class Binder implements IBinder {
return false;
}
+ /**
+ * Implemented to call the more convenient version
+ * {@link #dump(FileDescriptor, PrintWriter, String[])}.
+ */
+ public void dump(FileDescriptor fd, String[] args) {
+ FileOutputStream fout = new FileOutputStream(fd);
+ PrintWriter pw = new PrintWriter(fout);
+ try {
+ dump(fd, pw, args);
+ } finally {
+ pw.flush();
+ }
+ }
+
/**
* Print the object's state into the given stream.
*
@@ -302,6 +313,17 @@ final class BinderProxy implements IBinder {
throws RemoteException;
public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
+ public void dump(FileDescriptor fd, String[] args) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeFileDescriptor(fd);
+ data.writeStringArray(args);
+ try {
+ transact(DUMP_TRANSACTION, data, null, 0);
+ } finally {
+ data.recycle();
+ }
+ }
+
BinderProxy() {
mSelf = new WeakReference(this);
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index cdf907b1ad05d..467c17f7d3a19 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -26,6 +26,9 @@ public class Build {
/** Either a changelist number, or a label like "M4-rc20". */
public static final String ID = getString("ro.build.id");
+ /** A build ID string meant for displaying to the user */
+ public static final String DISPLAY = getString("ro.build.display.id");
+
/** The name of the overall product. */
public static final String PRODUCT = getString("ro.product.name");
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 5f7f91f4b9b07..950bb09d03c4c 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -17,6 +17,7 @@
package android.os;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
@@ -28,12 +29,13 @@ import dalvik.bytecode.Opcodes;
import dalvik.system.VMDebug;
-/** Provides various debugging functions for Android applications, including
+/**
+ * Provides various debugging functions for Android applications, including
* tracing and allocation counts.
* Logging Trace Files Debug can create log files that give details about an application, such as
* a call stack and start/stop times for any running methods. See Running the Traceview Debugging Program for
+href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer for
* information about reading trace files. To start logging trace files, call one
* of the startMethodTracing() methods. To stop tracing, call
* {@link #stopMethodTracing()}.
@@ -285,7 +287,7 @@ public final class Debug
/**
* Start method tracing with default log name and buffer size. See Running the Traceview Debugging Program for
+href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer for
* information about reading these files. Call stopMethodTracing() to stop
* tracing.
*/
@@ -297,7 +299,7 @@ href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Progra
* Start method tracing, specifying the trace log file name. The trace
* file will be put under "/sdcard" unless an absolute path is given.
* See Running the Traceview Debugging Program for
+ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer for
* information about reading trace files.
*
* @param traceName Name for the trace log file to create.
@@ -313,7 +315,7 @@ href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Progra
* Start method tracing, specifying the trace log file name and the
* buffer size. The trace files will be put under "/sdcard" unless an
* absolute path is given. See Running the Traceview Debugging Program for
+ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer for
* information about reading trace files.
* @param traceName Name for the trace log file to create.
* If no name argument is given, this value defaults to "/sdcard/dmtrace.trace".
@@ -330,7 +332,7 @@ href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Progra
* Start method tracing, specifying the trace log file name and the
* buffer size. The trace files will be put under "/sdcard" unless an
* absolute path is given. See Running the Traceview Debugging Program for
+ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer for
* information about reading trace files.
*
*
@@ -580,6 +582,18 @@ href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Progra
return VMDebug.getLoadedClassCount();
}
+ /**
+ * Dump "hprof" data to the specified file. This will cause a GC.
+ *
+ * @param fileName Full pathname of output file (e.g. "/sdcard/dump.hprof").
+ * @throws UnsupportedOperationException if the VM was built without
+ * HPROF support.
+ * @throws IOException if an error occurs while opening or writing files.
+ */
+ public static void dumpHprofData(String fileName) throws IOException {
+ VMDebug.dumpHprofData(fileName);
+ }
+
/**
* Returns the number of sent transactions from this process.
* @return The number of sent transactions or -1 if it could not read t.
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index e37b551903adc..f761e8e3d7138 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -74,6 +74,18 @@ public class Environment {
*/
public static final String MEDIA_UNMOUNTED = "unmounted";
+ /**
+ * getExternalStorageState() returns MEDIA_CHECKING if the media is present
+ * and being disk-checked
+ */
+ public static final String MEDIA_CHECKING = "checking";
+
+ /**
+ * getExternalStorageState() returns MEDIA_NOFS if the media is present
+ * but is blank or is using an unsupported filesystem
+ */
+ public static final String MEDIA_NOFS = "nofs";
+
/**
* getExternalStorageState() returns MEDIA_MOUNTED if the media is present
* and mounted at its mount point with read/write access.
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 3ec0e9b4db675..5c40c9a041ad8 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -16,6 +16,9 @@
package android.os;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* Base interface for a remotable object, the core part of a lightweight
* remote procedure call mechanism designed for high performance when
@@ -144,6 +147,14 @@ public interface IBinder {
*/
public IInterface queryLocalInterface(String descriptor);
+ /**
+ * Print the object's state into the given stream.
+ *
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param args additional arguments to the dump request.
+ */
+ public void dump(FileDescriptor fd, String[] args) throws RemoteException;
+
/**
* Perform a generic operation with the object.
*
diff --git a/core/java/android/os/ICheckinService.aidl b/core/java/android/os/ICheckinService.aidl
index 70ad28e4e4698..11becc4869c7f 100644
--- a/core/java/android/os/ICheckinService.aidl
+++ b/core/java/android/os/ICheckinService.aidl
@@ -26,6 +26,9 @@ import android.os.IParentalControlCallback;
* {@hide}
*/
interface ICheckinService {
+ /** Synchronously attempt a checkin with the server, return true on success. */
+ boolean checkin();
+
/** Direct submission of crash data; returns after writing the crash. */
void reportCrashSync(in byte[] crashData);
diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/IMountService.aidl
index 0397446155bb1..88dae8569f181 100644
--- a/core/java/android/os/IMountService.aidl
+++ b/core/java/android/os/IMountService.aidl
@@ -48,4 +48,19 @@ interface IMountService
* Safely unmount external storage at given mount point.
*/
void unmountMedia(String mountPoint);
+
+ /**
+ * Format external storage given a mount point
+ */
+ void formatMedia(String mountPoint);
+
+ /**
+ * Returns true if media notification sounds are enabled.
+ */
+ boolean getPlayNotificationSounds();
+
+ /**
+ * Sets whether or not media notification sounds are played.
+ */
+ void setPlayNotificationSounds(boolean value);
}
diff --git a/core/java/android/os/INetStatService.aidl b/core/java/android/os/INetStatService.aidl
index fb840d84fab34..a8f3de0e044c6 100644
--- a/core/java/android/os/INetStatService.aidl
+++ b/core/java/android/os/INetStatService.aidl
@@ -17,14 +17,19 @@
package android.os;
/**
- * Retrieves packet and byte counts for the phone data interface.
+ * Retrieves packet and byte counts for the phone data interface,
+ * and for all interfaces.
* Used for the data activity icon and the phone status in Settings.
*
* {@hide}
*/
interface INetStatService {
- int getTxPackets();
- int getRxPackets();
- int getTxBytes();
- int getRxBytes();
+ long getMobileTxPackets();
+ long getMobileRxPackets();
+ long getMobileTxBytes();
+ long getMobileRxBytes();
+ long getTotalTxPackets();
+ long getTotalRxPackets();
+ long getTotalTxBytes();
+ long getTotalRxBytes();
}
diff --git a/core/java/android/os/NetStat.java b/core/java/android/os/NetStat.java
index 7312236d743eb..733137a32c242 100644
--- a/core/java/android/os/NetStat.java
+++ b/core/java/android/os/NetStat.java
@@ -16,36 +16,197 @@
package android.os;
+import android.util.Log;
+
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.io.IOException;
+
/** @hide */
public class NetStat{
/**
- * Get total number of tx packets sent through ppp0
+ * Get total number of tx packets sent through rmnet0 or ppp0
*
- * @return number of Tx packets through ppp0
+ * @return number of Tx packets through rmnet0 or ppp0
*/
-
- public native static int netStatGetTxPkts();
+ public static long getMobileTxPkts() {
+ return getMobileStat("tx_packets");
+ }
/**
- * Get total number of rx packets received through ppp0
+ * Get total number of rx packets received through rmnet0 or ppp0
*
- * @return number of Rx packets through ppp0
+ * @return number of Rx packets through rmnet0 or ppp0
*/
- public native static int netStatGetRxPkts();
+ public static long getMobileRxPkts() {
+ return getMobileStat("rx_packets");
+ }
/**
- * Get total number of tx bytes received through ppp0
+ * Get total number of tx bytes received through rmnet0 or ppp0
*
- * @return number of Tx bytes through ppp0
+ * @return number of Tx bytes through rmnet0 or ppp0
*/
- public native static int netStatGetTxBytes();
+ public static long getMobileTxBytes() {
+ return getMobileStat("tx_bytes");
+ }
/**
- * Get total number of rx bytes received through ppp0
+ * Get total number of rx bytes received through rmnet0 or ppp0
*
- * @return number of Rx bytes through ppp0
+ * @return number of Rx bytes through rmnet0 or ppp0
*/
- public native static int netStatGetRxBytes();
+ public static long getMobileRxBytes() {
+ return getMobileStat("rx_bytes");
+ }
+ /**
+ * Get the total number of packets sent through all network interfaces.
+ *
+ * @return the number of packets sent through all network interfaces
+ */
+ public static long getTotalTxPkts() {
+ return getTotalStat("tx_packets");
+ }
+
+ /**
+ * Get the total number of packets received through all network interfaces.
+ *
+ * @return the number of packets received through all network interfaces
+ */
+ public static long getTotalRxPkts() {
+ return getTotalStat("rx_packets");
+ }
+
+ /**
+ * Get the total number of bytes sent through all network interfaces.
+ *
+ * @return the number of bytes sent through all network interfaces
+ */
+ public static long getTotalTxBytes() {
+ return getTotalStat("tx_bytes");
+ }
+
+ /**
+ * Get the total number of bytes received through all network interfaces.
+ *
+ * @return the number of bytes received through all network interfaces
+ */
+ public static long getTotalRxBytes() {
+ return getTotalStat("rx_bytes");
+ }
+
+ /**
+ * Gets network bytes sent for this UID.
+ * The statistics are across all interfaces.
+ * The statistics come from /proc/uid_stat.
+ *
+ * {@see android.os.Process#myUid()}.
+ *
+ * @param uid
+ * @return byte count
+ */
+ public static long getUidTxBytes(int uid) {
+ return getNumberFromFilePath("/proc/uid_stat/" + uid + "/tcp_snd");
+ }
+
+ /**
+ * Gets network bytes received for this UID.
+ * The statistics are across all interfaces.
+ * The statistics come from /proc/uid_stat.
+ *
+ * {@see android.os.Process#myUid()}.
+ *
+ * @param uid
+ * @return byte count
+ */
+ public static long getUidRxBytes(int uid) {
+ return getNumberFromFilePath("/proc/uid_stat/" + uid + "/tcp_rcv");
+ }
+
+ private static String TAG = "netstat";
+ private static final byte[] buf = new byte[16];
+
+ private static long getTotalStat(String whatStat) {
+ File netdir = new File("/sys/class/net");
+
+ File[] nets = netdir.listFiles();
+ if (nets == null) {
+ return 0;
+ }
+ long total = 0;
+ StringBuffer strbuf = new StringBuffer();
+ for (File net : nets) {
+ strbuf.append(net.getPath()).append(File.separator).append("statistics")
+ .append(File.separator).append(whatStat);
+ total += getNumberFromFilePath(strbuf.toString());
+ strbuf.setLength(0);
+ }
+ return total;
+ }
+
+ private static long getMobileStat(String whatStat) {
+ String filename = "/sys/class/net/rmnet0/statistics/" + whatStat;
+ RandomAccessFile raf = getFile(filename);
+ if (raf == null) {
+ filename = "/sys/class/net/ppp0/statistics/" + whatStat;
+ raf = getFile(filename);
+ }
+ if (raf == null) {
+ return 0L;
+ }
+ return getNumberFromFile(raf, filename);
+ }
+
+ // File will have format
*
*
*
*
* <!-- Search Activity - searchable -->
* <activity android:name="MySearchActivity"
@@ -765,9 +766,8 @@ import android.view.KeyEvent;
*
* <!-- Content provider for search suggestions -->
@@ -832,7 +832,7 @@ import android.view.KeyEvent;
*
*
*
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 6c08e750c7d7a..72692f4ebbf94 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
- * Threading section of the
- * Application Model overview.
+ * work. More information on this can be found in
+ * Application Fundamentals:
+ * Processes and Threads.
*
* android:searchSuggestAuthority
* This value must match the authority string provided in the provider section
- * of your manifest.
+ * of your manifest.
* Yes
*
@@ -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 written
+ * interface that has been written
* in aidl.
*
*
+ *
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED";
@@ -1114,22 +1120,48 @@ public class Intent implements Parcelable {
* Broadcast Action: An existing application package has been removed from
* the device. The data contains the name of the package. The package
* that is being installed does not receive this Intent.
+ *
+ *
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
/**
* Broadcast Action: An existing application package has been changed (e.g. a component has been
* enabled or disabled. The data contains the name of the package.
+ *
+ *
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
/**
- * Broadcast Action: The user has restarted a package, all runtime state
+ * Broadcast Action: The user has restarted a package, and all of its
+ * processes have been killed. All runtime state
* associated with it (processes, alarms, notifications, etc) should
- * be remove. The data contains the name of the package.
+ * be removed. The data contains the name of the package.
+ *
+ *
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
+ /**
+ * Broadcast Action: The user has cleared the data of a package. This should
+ * be preceded by {@link #ACTION_PACKAGE_RESTARTED}, after which all of
+ * its persistent data is erased and this broadcast sent. The data contains
+ * the name of the package.
+ *
+ *
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED";
/**
* Broadcast Action: A user ID has been removed from the system. The user
* ID number is stored in the extra data under {@link #EXTRA_UID}.
@@ -1227,7 +1259,6 @@ public class Intent implements Parcelable {
/**
* Broadcast Action: External media is present, and being disk-checked
* The path to the mount point for the checking media is contained in the Intent.mData field.
- * @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_MEDIA_CHECKING = "android.intent.action.MEDIA_CHECKING";
@@ -1235,7 +1266,6 @@ public class Intent implements Parcelable {
/**
* Broadcast Action: External media is present, but is using an incompatible fs (or is blank)
* The path to the mount point for the checking media is contained in the Intent.mData field.
- * @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_MEDIA_NOFS = "android.intent.action.MEDIA_NOFS";
@@ -1655,6 +1685,22 @@ public class Intent implements Parcelable {
*/
public static final String EXTRA_UID = "android.intent.extra.UID";
+ /**
+ * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED}
+ * intents to indicate whether this represents a full uninstall (removing
+ * both the code and its data) or a partial uninstall (leaving its data,
+ * implying that this is an update).
+ */
+ public static final String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
+
+ /**
+ * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED}
+ * intents to indicate that this is a replacement of the package, so this
+ * broadcast will immediately be followed by an add broadcast for a
+ * different version of the same package.
+ */
+ public static final String EXTRA_REPLACING = "android.intent.extra.REPLACING";
+
/**
* Used as an int extra field in {@link android.app.AlarmManager} intents
* to tell the application being invoked how many pending alarms are being
@@ -1770,8 +1816,8 @@ public class Intent implements Parcelable {
* Intent, resulting in the stack now being: A, B.
*
*
+ *
+ */
+ public static void deleteAllHosts() {
+ try {
+ sService.deleteAllHosts();
+ }
+ catch (RemoteException e) {
+ throw new RuntimeException("system server dead?", e);
+ }
}
public final GadgetHostView createView(Context context, int gadgetId, GadgetInfo gadget) {
GadgetHostView view = onCreateView(context, gadgetId, gadget);
view.setGadget(gadgetId, gadget);
- view.updateGadget(null);
+ synchronized (mViews) {
+ mViews.put(gadgetId, view);
+ }
+ RemoteViews views = null;
+ try {
+ views = sService.getGadgetViews(gadgetId);
+ } catch (RemoteException e) {
+ throw new RuntimeException("system server dead?", e);
+ }
+ view.updateGadget(views);
return view;
}
@@ -68,5 +206,16 @@ public class GadgetHost {
protected GadgetHostView onCreateView(Context context, int gadgetId, GadgetInfo gadget) {
return new GadgetHostView(context);
}
+
+ void updateGadgetView(int gadgetId, RemoteViews views) {
+ GadgetHostView v;
+ synchronized (mViews) {
+ v = mViews.get(gadgetId);
+ }
+ if (v != null) {
+ v.updateGadget(views);
+ }
+ }
}
+
diff --git a/core/java/android/gadget/GadgetHostView.java b/core/java/android/gadget/GadgetHostView.java
index e2bef8c12e40b..d92c1236f078b 100644
--- a/core/java/android/gadget/GadgetHostView.java
+++ b/core/java/android/gadget/GadgetHostView.java
@@ -19,16 +19,21 @@ package android.gadget;
import android.content.Context;
import android.content.pm.PackageManager;
import android.gadget.GadgetInfo;
-import android.util.AttributeSet;
+import android.graphics.Color;
+import android.util.Config;
import android.util.Log;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RemoteViews;
import android.widget.TextView;
+import android.widget.ViewAnimator;
-public class GadgetHostView extends FrameLayout {
+public class GadgetHostView extends ViewAnimator {
static final String TAG = "GadgetHostView";
+ static final boolean LOGD = Config.LOGD || true;
// When we're inflating the initialLayout for a gadget, we only allow
// views that are allowed in RemoteViews.
@@ -40,63 +45,139 @@ public class GadgetHostView extends FrameLayout {
int mGadgetId;
GadgetInfo mInfo;
- View mContentView;
+ View mActiveView;
+ View mStaleView;
+
+ protected int mDefaultGravity = Gravity.CENTER;
public GadgetHostView(Context context) {
super(context);
}
public void setGadget(int gadgetId, GadgetInfo info) {
+ if (LOGD) Log.d(TAG, "setGadget is incoming with info=" + info);
if (mInfo != null) {
// TODO: remove the old view, or whatever
}
mGadgetId = gadgetId;
mInfo = info;
+
+ View defaultView = getDefaultView();
+ flipUpdate(defaultView);
+ }
+
+ /**
+ * Trigger actual animation between current and new content in the
+ * {@link ViewAnimator}.
+ */
+ protected void flipUpdate(View newContent) {
+ if (LOGD) Log.d(TAG, "pushing an update to surface");
+
+ // Take requested dimensions from parent, but apply default gravity.
+ ViewGroup.LayoutParams requested = newContent.getLayoutParams();
+ if (requested == null) {
+ requested = new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT,
+ LayoutParams.FILL_PARENT);
+ }
+
+ FrameLayout.LayoutParams params =
+ new FrameLayout.LayoutParams(requested.width, requested.height);
+ params.gravity = mDefaultGravity;
+ newContent.setLayoutParams(params);
+
+ // Add new content and animate to it
+ addView(newContent);
+ showNext();
+
+ // Dispose old stale view
+ removeView(mStaleView);
+ mStaleView = mActiveView;
+ mActiveView = newContent;
}
+ /**
+ * Process a set of {@link RemoteViews} coming in as an update from the
+ * gadget provider. Will animate into these new views as needed.
+ */
public void updateGadget(RemoteViews remoteViews) {
- Context context = getContext();
-
- View contentView = null;
+ if (LOGD) Log.d(TAG, "updateGadget() with remoteViews = " + remoteViews);
+
+ View newContent = null;
Exception exception = null;
+
try {
if (remoteViews == null) {
// there is no remoteViews (yet), so use the initial layout
- Context theirContext = context.createPackageContext(mInfo.provider.getPackageName(),
- 0);
- LayoutInflater inflater = (LayoutInflater)theirContext.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- inflater = inflater.cloneInContext(theirContext);
- inflater.setFilter(sInflaterFilter);
- contentView = inflater.inflate(mInfo.initialLayout, this, false);
+ newContent = getDefaultView();
} else {
// use the RemoteViews
- contentView = remoteViews.apply(mContext, this);
+ // TODO: try applying RemoteViews to existing staleView if available
+ newContent = remoteViews.apply(mContext, this);
}
- }
- catch (PackageManager.NameNotFoundException e) {
+ } catch (RuntimeException e) {
exception = e;
}
- catch (RuntimeException e) {
- exception = e;
- }
- if (contentView == null) {
+
+ if (exception != null && LOGD) {
Log.w(TAG, "Error inflating gadget " + mInfo, exception);
+ }
+
+ if (newContent == null) {
// TODO: Should we throw an exception here for the host activity to catch?
// Maybe we should show a generic error widget.
- TextView tv = new TextView(context);
- tv.setText("Error inflating gadget");
- contentView = tv;
+ if (LOGD) Log.d(TAG, "updateGadget couldn't find any view, so inflating error");
+ newContent = getErrorView();
}
- FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.WRAP_CONTENT,
- FrameLayout.LayoutParams.WRAP_CONTENT);
-
- mContentView = contentView;
- this.addView(contentView, lp);
-
- // TODO: do an animation (maybe even one provided by the gadget).
+ flipUpdate(newContent);
+ }
+
+ /**
+ * Inflate and return the default layout requested by gadget provider.
+ */
+ protected View getDefaultView() {
+ View defaultView = null;
+ Exception exception = null;
+
+ try {
+ if (mInfo != null) {
+ Context theirContext = mContext.createPackageContext(
+ mInfo.provider.getPackageName(), 0 /* no flags */);
+ LayoutInflater inflater = (LayoutInflater)
+ theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater = inflater.cloneInContext(theirContext);
+ inflater.setFilter(sInflaterFilter);
+ defaultView = inflater.inflate(mInfo.initialLayout, this, false);
+ } else {
+ Log.w(TAG, "can't inflate defaultView because mInfo is missing");
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ exception = e;
+ } catch (RuntimeException e) {
+ exception = e;
+ }
+
+ if (exception != null && LOGD) {
+ Log.w(TAG, "Error inflating gadget " + mInfo, exception);
+ }
+
+ if (defaultView == null) {
+ if (LOGD) Log.d(TAG, "getDefaultView couldn't find any view, so inflating error");
+ defaultView = getErrorView();
+ }
+
+ return defaultView;
+ }
+
+ /**
+ * Inflate and return a view that represents an error state.
+ */
+ protected View getErrorView() {
+ TextView tv = new TextView(mContext);
+ // TODO: move this error string and background color into resources
+ tv.setText("Error inflating gadget");
+ tv.setBackgroundColor(Color.argb(127, 0, 0, 0));
+ return tv;
}
}
diff --git a/core/java/android/gadget/GadgetInfo.java b/core/java/android/gadget/GadgetInfo.java
index 1a7a9a0ef0443..5ac3da9cf2d11 100644
--- a/core/java/android/gadget/GadgetInfo.java
+++ b/core/java/android/gadget/GadgetInfo.java
@@ -58,6 +58,16 @@ public class GadgetInfo implements Parcelable {
*/
public ComponentName configure;
+ /**
+ * The label to display to the user.
+ */
+ public String label;
+
+ /**
+ * The icon to display for this gadget in the picker list.
+ */
+ public int icon;
+
public GadgetInfo() {
}
@@ -75,6 +85,8 @@ public class GadgetInfo implements Parcelable {
if (0 != in.readInt()) {
this.configure = new ComponentName(in);
}
+ this.label = in.readString();
+ this.icon = in.readInt();
}
@@ -95,6 +107,8 @@ public class GadgetInfo implements Parcelable {
} else {
out.writeInt(0);
}
+ out.writeString(this.label);
+ out.writeInt(this.icon);
}
public int describeContents() {
diff --git a/core/java/android/gadget/GadgetManager.java b/core/java/android/gadget/GadgetManager.java
index 088dc86f9d588..20f40140cbdbe 100644
--- a/core/java/android/gadget/GadgetManager.java
+++ b/core/java/android/gadget/GadgetManager.java
@@ -30,6 +30,10 @@ import java.lang.ref.WeakReference;
import java.util.List;
import java.util.WeakHashMap;
+/**
+ * Updates gadget state; gets information about installed gadget providers and other
+ * gadget related state.
+ */
public class GadgetManager {
static final String TAG = "GadgetManager";
@@ -40,9 +44,8 @@ public class GadgetManager {
* The system will respond with an onActivityResult call with the following extras in
* the intent:
*
- *
* TODO: Add constants for these.
* TODO: Where does this go?
@@ -50,21 +53,42 @@ public class GadgetManager {
public static final String GADGET_PICK_ACTION = "android.gadget.action.PICK_GADGET";
public static final String EXTRA_GADGET_ID = "gadgetId";
+ public static final String EXTRA_GADGET_IDS = "gadgetIds";
+ public static final String EXTRA_HOST_ID = "hostId";
/**
* Sent when it is time to update your gadget.
+ *
+ * Sample Code
+ * For an example of how to write a gadget provider, see the
+ * android.gadget
+ * package overview.
+ */
+public class GadgetProvider extends BroadcastReceiver {
+ /**
+ * Constructor to initialize GadgetProvider.
+ */
+ public GadgetProvider() {
+ }
+
+ /**
+ * Implements {@link BroadcastReceiver#onReceive} to dispatch calls to the various
+ * other methods on GadgetProvider.
+ *
+ * @param context The Context in which the receiver is running.
+ * @param intent The Intent being received.
+ */
+ // BEGIN_INCLUDE(onReceive)
+ public void onReceive(Context context, Intent intent) {
+ // Protect against rogue update broadcasts (not really a security issue,
+ // just filter bad broacasts out so subclasses are less likely to crash).
+ String action = intent.getAction();
+ if (GadgetManager.GADGET_UPDATE_ACTION.equals(action)) {
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ int[] gadgetIds = extras.getIntArray(GadgetManager.EXTRA_GADGET_IDS);
+ if (gadgetIds != null && gadgetIds.length > 0) {
+ this.onUpdate(context, GadgetManager.getInstance(context), gadgetIds);
+ }
+ }
+ }
+ else if (GadgetManager.GADGET_DELETED_ACTION.equals(action)) {
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ int[] gadgetIds = extras.getIntArray(GadgetManager.EXTRA_GADGET_IDS);
+ if (gadgetIds != null && gadgetIds.length > 0) {
+ this.onDeleted(context, gadgetIds);
+ }
+ }
+ }
+ else if (GadgetManager.GADGET_ENABLED_ACTION.equals(action)) {
+ this.onEnabled(context);
+ }
+ else if (GadgetManager.GADGET_DISABLED_ACTION.equals(action)) {
+ this.onDisabled(context);
+ }
+ }
+ // END_INCLUDE(onReceive)
+
+ /**
+ * Called in response to the {@link GadgetManager#GADGET_UPDATE_ACTION} broadcast when
+ * this gadget provider is being asked to provide {@link android.widget.RemoteViews RemoteViews}
+ * for a set of gadgets. Override this method to implement your own gadget functionality.
+ *
+ * {@more}
+ * Gadget Providers
+
+
+Gadget Hosts
+
+
+{@more}
+Gadget Providers
+Declaring a gadget in the AndroidManifest
+
+Adding the {@link android.gadget.GadgetInfo GadgetInfo} meta-data
+
+Using the {@link android.gadget.GadgetProvider GadgetProvider} class
+
+Gadget Configuration UI
+
+Gadget Broadcast Intents
+
+Gadget Hosts
+
+ *
+ *
*