diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 14:04:24 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 14:04:24 -0800 |
commit | 076357b8567458d4b6dfdcf839ef751634cd2bfb (patch) | |
tree | efbb2fd6f1dc67d2d606382fc3b82983e7cb2e1f /core/java | |
parent | 3dec7d563a2f3e1eb967ce2054a00b6620e3558c (diff) | |
download | frameworks_base-076357b8567458d4b6dfdcf839ef751634cd2bfb.zip frameworks_base-076357b8567458d4b6dfdcf839ef751634cd2bfb.tar.gz frameworks_base-076357b8567458d4b6dfdcf839ef751634cd2bfb.tar.bz2 |
auto import from //depot/cupcake/@132589
Diffstat (limited to 'core/java')
95 files changed, 1538 insertions, 5114 deletions
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java index 3b5ad86..394b8e3 100644 --- a/core/java/android/app/ApplicationContext.java +++ b/core/java/android/app/ApplicationContext.java @@ -1487,7 +1487,7 @@ class ApplicationContext extends Context { static final class ApplicationPackageManager extends PackageManager { @Override public PackageInfo getPackageInfo(String packageName, int flags) - throws NameNotFoundException { + throws NameNotFoundException { try { PackageInfo pi = mPM.getPackageInfo(packageName, flags); if (pi != null) { @@ -1500,43 +1500,6 @@ class ApplicationContext extends Context { throw new NameNotFoundException(packageName); } - public Intent getLaunchIntentForPackage(String packageName) - throws NameNotFoundException { - // First see if the package has an INFO activity; the existence of - // such an activity is implied to be the desired front-door for the - // overall package (such as if it has multiple launcher entries). - Intent intent = getLaunchIntentForPackageCategory(this, packageName, - Intent.CATEGORY_INFO); - if (intent != null) { - return intent; - } - - // Otherwise, try to find a main launcher activity. - return getLaunchIntentForPackageCategory(this, packageName, - Intent.CATEGORY_LAUNCHER); - } - - // XXX This should be implemented as a call to the package manager, - // to reduce the work needed. - static Intent getLaunchIntentForPackageCategory(PackageManager pm, - String packageName, String category) { - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - Intent intentToResolve = new Intent(Intent.ACTION_MAIN, null); - intentToResolve.addCategory(category); - final List<ResolveInfo> apps = - pm.queryIntentActivities(intentToResolve, 0); - // I wish there were a way to directly get the "main" activity of a - // package but ... - for (ResolveInfo app : apps) { - if (app.activityInfo.packageName.equals(packageName)) { - intent.setClassName(packageName, app.activityInfo.name); - return intent; - } - } - return null; - } - @Override public int[] getPackageGids(String packageName) throws NameNotFoundException { diff --git a/core/java/android/app/ExpandableListActivity.java b/core/java/android/app/ExpandableListActivity.java index a2e048f..75dfcae 100644 --- a/core/java/android/app/ExpandableListActivity.java +++ b/core/java/android/app/ExpandableListActivity.java @@ -63,21 +63,21 @@ import java.util.Map; * * <pre> * <?xml version="1.0" encoding="UTF-8"?> - * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + * <LinearLayout * android:orientation="vertical" * android:layout_width="fill_parent" * android:layout_height="fill_parent" - * android:paddingLeft="8dp" - * android:paddingRight="8dp"> + * android:paddingLeft="8" + * android:paddingRight="8"> * - * <ExpandableListView android:id="@id/android:list" + * <ExpandableListView id="android:list" * android:layout_width="fill_parent" * android:layout_height="fill_parent" * android:background="#00FF00" * android:layout_weight="1" * android:drawSelectorOnTop="false"/> * - * <TextView android:id="@id/android:empty" + * <TextView id="android:empty" * android:layout_width="fill_parent" * android:layout_height="fill_parent" * android:background="#FF0000" @@ -113,19 +113,19 @@ import java.util.Map; * * <pre> * <?xml version="1.0" encoding="utf-8"?> - * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + * <LinearLayout * android:layout_width="fill_parent" * android:layout_height="wrap_content" * android:orientation="vertical"> * - * <TextView android:id="@+id/text1" - * android:textSize="16sp" + * <TextView id="text1" + * android:textSize="16" * android:textStyle="bold" * android:layout_width="fill_parent" * android:layout_height="wrap_content"/> * - * <TextView android:id="@+id/text2" - * android:textSize="16sp" + * <TextView id="text2" + * android:textSize="16" * android:layout_width="fill_parent" * android:layout_height="wrap_content"/> * </LinearLayout> @@ -162,8 +162,7 @@ public class ExpandableListActivity extends Activity implements /** * Override this to populate the context menu when an item is long pressed. menuInfo - * will contain an {@link android.widget.ExpandableListView.ExpandableListContextMenuInfo} - * whose packedPosition is a packed position + * will contain a {@link AdapterContextMenuInfo} whose position is a packed position * that should be used with {@link ExpandableListView#getPackedPositionType(long)} and * the other similar methods. * <p> diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java deleted file mode 100644 index 2b12a2a..0000000 --- a/core/java/android/app/IntentService.java +++ /dev/null @@ -1,74 +0,0 @@ -package android.app; - -import android.content.Intent; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; - -/** - * An abstract {@link Service} that serializes the handling of the Intents passed upon service - * start and handles them on a handler thread. - * - * <p>To use this class extend it and implement {@link #onHandleIntent}. The {@link Service} will - * automatically be stopped when the last enqueued {@link Intent} is handled. - */ -public abstract class IntentService extends Service { - private volatile Looper mServiceLooper; - private volatile ServiceHandler mServiceHandler; - private String mName; - - private final class ServiceHandler extends Handler { - public ServiceHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - onHandleIntent((Intent)msg.obj); - stopSelf(msg.arg1); - } - } - - public IntentService(String name) { - super(); - mName = name; - } - - @Override - public void onCreate() { - super.onCreate(); - HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); - thread.start(); - - mServiceLooper = thread.getLooper(); - mServiceHandler = new ServiceHandler(mServiceLooper); - } - - @Override - public void onStart(Intent intent, int startId) { - super.onStart(intent, startId); - Message msg = mServiceHandler.obtainMessage(); - msg.arg1 = startId; - msg.obj = intent; - mServiceHandler.sendMessage(msg); - } - - @Override - public void onDestroy() { - mServiceLooper.quit(); - } - - @Override - public IBinder onBind(Intent intent) { - return null; - } - - /** - * Invoked on the Handler thread with the {@link Intent} that is passed to {@link #onStart}. - * Note that this will be invoked from a different thread than the one that handles the - * {@link #onStart} call. - */ - protected abstract void onHandleIntent(Intent intent); -} diff --git a/core/java/android/app/ListActivity.java b/core/java/android/app/ListActivity.java index 5523c18..2818937 100644 --- a/core/java/android/app/ListActivity.java +++ b/core/java/android/app/ListActivity.java @@ -53,22 +53,22 @@ import android.widget.ListView; * </p> * * <pre> - * <?xml version="1.0" encoding="utf-8"?> - * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + * <?xml version="1.0" encoding="UTF-8"?> + * <LinearLayout * android:orientation="vertical" * android:layout_width="fill_parent" * android:layout_height="fill_parent" - * android:paddingLeft="8dp" - * android:paddingRight="8dp"> + * android:paddingLeft="8" + * android:paddingRight="8"> * - * <ListView android:id="@id/android:list" + * <ListView id="android:list" * android:layout_width="fill_parent" * android:layout_height="fill_parent" * android:background="#00FF00" * android:layout_weight="1" * android:drawSelectorOnTop="false"/> * - * <TextView id="@id/android:empty" + * <TextView id="android:empty" * android:layout_width="fill_parent" * android:layout_height="fill_parent" * android:background="#FF0000" @@ -99,19 +99,19 @@ import android.widget.ListView; * * <pre> * <?xml version="1.0" encoding="utf-8"?> - * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + * <LinearLayout * android:layout_width="fill_parent" * android:layout_height="wrap_content" * android:orientation="vertical"> * - * <TextView android:id="@+id/text1" - * android:textSize="16sp" + * <TextView id="text1" + * android:textSize="16" * android:textStyle="bold" * android:layout_width="fill_parent" * android:layout_height="wrap_content"/> * - * <TextView android:id="@+id/text2" - * android:textSize="16sp" + * <TextView id="text2" + * android:textSize="16" * android:layout_width="fill_parent" * android:layout_height="wrap_content"/> * </LinearLayout> @@ -142,8 +142,8 @@ import android.widget.ListView; * public class MyListAdapter extends ListActivity { * * @Override - * protected void onCreate(Bundle savedInstanceState){ - * super.onCreate(savedInstanceState); + * protected void onCreate(Bundle icicle){ + * super.onCreate(icicle); * * // We'll define a custom screen layout here (the one shown above), but * // typically, you could just use the standard ListActivity layout. diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 39edab7..afb3827 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -82,7 +82,9 @@ public class NotificationManager * @param id An identifier for this notification unique within your * application. * @param notification A {@link Notification} object describing how to - * notify the user, other than the view you're providing. Must not be null. + * notify the user, other than the view you're providing. If you + * pass null, there will be no persistent notification and no + * flashing, vibration, etc. */ public void notify(int id, Notification notification) { diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index d447eb2..7b8256c 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -448,7 +448,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } } mSearchTextField.setInputType(inputType); - mSearchTextField.setImeOptions(mSearchable.getImeOptions()); } } @@ -794,6 +793,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // otherwise, dispatch an "edit view" key switch (keyCode) { case KeyEvent.KEYCODE_ENTER: + case KeyEvent.KEYCODE_DPAD_CENTER: if (event.getAction() == KeyEvent.ACTION_UP) { v.cancelLongPress(); launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null); diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index c1d66f4..2cc6de9 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -747,14 +747,6 @@ import android.view.KeyEvent; * <a href="../R.attr.html#inputType">inputType</a> attribute.</td> * <td align="center">No</td> * </tr> - * <tr><th>android:imeOptions</th> - * <td>If provided, supplies additional options for the input method. - * For most searches, in which free form text is expected, this attribute - * need not be provided, and will default to "actionSearch". - * Suitable values for this attribute are described in the - * <a href="../R.attr.html#imeOptions">imeOptions</a> attribute.</td> - * <td align="center">No</td> - * </tr> * * </tbody> * </table> diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 1ba1c1e..56b231f 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -63,10 +63,8 @@ public class BluetoothDevice { public static final int UNBOND_REASON_AUTH_CANCELED = 3; /** A bond attempt failed because we could not contact the remote device */ public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; - /** A bond attempt failed because a discovery is in progress */ - public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; /** An existing bond was explicitly revoked */ - public static final int UNBOND_REASON_REMOVED = 6; + public static final int UNBOND_REASON_REMOVED = 5; private static final String TAG = "BluetoothDevice"; diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 25544de..3a64cee 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -18,7 +18,6 @@ package android.content; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; -import android.content.res.AssetFileDescriptor; import android.content.res.Configuration; import android.database.Cursor; import android.database.CursorToBulkCursorAdaptor; @@ -163,13 +162,6 @@ public abstract class ContentProvider implements ComponentCallbacks { return ContentProvider.this.openFile(uri, mode); } - public AssetFileDescriptor openAssetFile(Uri uri, String mode) - throws FileNotFoundException { - if (mode != null && mode.startsWith("rw")) checkWritePermission(uri); - else checkReadPermission(uri); - return ContentProvider.this.openAssetFile(uri, mode); - } - public ISyncAdapter getSyncAdapter() { checkWritePermission(null); return ContentProvider.this.getSyncAdapter().getISyncAdapter(); @@ -446,9 +438,8 @@ public abstract class ContentProvider implements ComponentCallbacks { * of this method should create a new ParcelFileDescriptor for each call. * * @param uri The URI whose file is to be opened. - * @param mode Access mode for the file. May be "r" for read-only access, - * "rw" for read and write access, or "rwt" for read and write access - * that truncates any existing file. + * @param mode Access mode for the file. May be "r" for read-only access + * or "rw" for read and write access. * * @return Returns a new ParcelFileDescriptor which you can use to access * the file. @@ -457,66 +448,19 @@ public abstract class ContentProvider implements ComponentCallbacks { * no file associated with the given URI or the mode is invalid. * @throws SecurityException Throws SecurityException if the caller does * not have permission to access the file. - * - * @see #openAssetFile(Uri, String) - * @see #openFileHelper(Uri, String) - */ + */ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { throw new FileNotFoundException("No files supported by provider at " + uri); } - - /** - * This is like {@link #openFile}, but can be implemented by providers - * that need to be able to return sub-sections of files, often assets - * inside of their .apk. Note that when implementing this your clients - * must be able to deal with such files, either directly with - * {@link ContentResolver#openAssetFileDescriptor - * ContentResolver.openAssetFileDescriptor}, or by using the higher-level - * {@link ContentResolver#openInputStream ContentResolver.openInputStream} - * or {@link ContentResolver#openOutputStream ContentResolver.openOutputStream} - * methods. - * - * <p><em>Note: if you are implementing this to return a full file, you - * should create the AssetFileDescriptor with - * {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with - * applications that can not handle sub-sections of files.</em></p> - * - * @param uri The URI whose file is to be opened. - * @param mode Access mode for the file. May be "r" for read-only access, - * "w" for write-only access (erasing whatever data is currently in - * the file), "wa" for write-only access to append to any existing data, - * "rw" for read and write access on any existing data, and "rwt" for read - * and write access that truncates any existing file. - * - * @return Returns a new AssetFileDescriptor which you can use to access - * the file. - * - * @throws FileNotFoundException Throws FileNotFoundException if there is - * no file associated with the given URI or the mode is invalid. - * @throws SecurityException Throws SecurityException if the caller does - * not have permission to access the file. - * - * @see #openFile(Uri, String) - * @see #openFileHelper(Uri, String) - */ - public AssetFileDescriptor openAssetFile(Uri uri, String mode) - throws FileNotFoundException { - ParcelFileDescriptor fd = openFile(uri, mode); - return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null; - } /** * Convenience for subclasses that wish to implement {@link #openFile} * by looking up a column named "_data" at the given URI. * * @param uri The URI to be opened. - * @param mode The file mode. May be "r" for read-only access, - * "w" for write-only access (erasing whatever data is currently in - * the file), "wa" for write-only access to append to any existing data, - * "rw" for read and write access on any existing data, and "rwt" for read - * and write access that truncates any existing file. + * @param mode The file mode. * * @return Returns a new ParcelFileDescriptor that can be used by the * client to access the file. @@ -545,7 +489,16 @@ public abstract class ContentProvider implements ComponentCallbacks { throw new FileNotFoundException("Column _data not found."); } - int modeBits = ContentResolver.modeToMode(uri, mode); + int modeBits; + if ("r".equals(mode)) { + modeBits = ParcelFileDescriptor.MODE_READ_ONLY; + } else if ("rw".equals(mode)) { + modeBits = ParcelFileDescriptor.MODE_READ_WRITE + | ParcelFileDescriptor.MODE_CREATE; + } else { + throw new FileNotFoundException("Bad mode for " + uri + ": " + + mode); + } return ParcelFileDescriptor.open(new File(path), modeBits); } diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index e5e3f74..ede2c9b 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -16,7 +16,6 @@ package android.content; -import android.content.res.AssetFileDescriptor; import android.database.BulkCursorNative; import android.database.BulkCursorToCursorAdaptor; import android.database.Cursor; @@ -188,25 +187,6 @@ abstract public class ContentProviderNative extends Binder implements IContentPr return true; } - case OPEN_ASSET_FILE_TRANSACTION: - { - data.enforceInterface(IContentProvider.descriptor); - Uri url = Uri.CREATOR.createFromParcel(data); - String mode = data.readString(); - - AssetFileDescriptor fd; - fd = openAssetFile(url, mode); - reply.writeNoException(); - if (fd != null) { - reply.writeInt(1); - fd.writeToParcel(reply, - Parcelable.PARCELABLE_WRITE_RETURN_VALUE); - } else { - reply.writeInt(0); - } - return true; - } - case GET_SYNC_ADAPTER_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); @@ -433,29 +413,6 @@ final class ContentProviderProxy implements IContentProvider return fd; } - public AssetFileDescriptor openAssetFile(Uri url, String mode) - throws RemoteException, FileNotFoundException { - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - - data.writeInterfaceToken(IContentProvider.descriptor); - - url.writeToParcel(data, 0); - data.writeString(mode); - - mRemote.transact(IContentProvider.OPEN_ASSET_FILE_TRANSACTION, data, reply, 0); - - DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); - int has = reply.readInt(); - AssetFileDescriptor fd = has != 0 - ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null; - - data.recycle(); - reply.recycle(); - - return fd; - } - public ISyncAdapter getSyncAdapter() throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 0d886ee..52f55b6 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -17,7 +17,6 @@ package android.content; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.AssetFileDescriptor; import android.content.res.Resources; import android.database.ContentObserver; import android.database.Cursor; @@ -29,7 +28,6 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.text.TextUtils; -import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; @@ -172,100 +170,119 @@ public abstract class ContentResolver { * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li> * <li>file ({@link #SCHEME_FILE})</li> * </ul> - * - * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information - * on these schemes. - * - * @param uri The desired URI. + * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5> + * <p> + * A Uri object can be used to reference a resource in an APK file. The + * Uri should be one of the following formats: + * <ul> + * <li><code>android.resource://package_name/id_number</code><br/> + * <code>package_name</code> is your package name as listed in your AndroidManifest.xml. + * For example <code>com.example.myapp</code><br/> + * <code>id_number</code> is the int form of the ID.<br/> + * The easiest way to construct this form is + * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre> + * </li> + * <li><code>android.resource://package_name/type/name</code><br/> + * <code>package_name</code> is your package name as listed in your AndroidManifest.xml. + * For example <code>com.example.myapp</code><br/> + * <code>type</code> is the string form of the resource type. For example, <code>raw</code> + * or <code>drawable</code>. + * <code>name</code> is the string form of the resource name. That is, whatever the file + * name was in your res directory, without the type extension. + * The easiest way to construct this form is + * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre> + * </li> + * </ul> + * @param uri The desired "content:" URI. * @return InputStream * @throws FileNotFoundException if the provided URI could not be opened. - * @see #openAssetFileDescriptor(Uri, String) */ public final InputStream openInputStream(Uri uri) throws FileNotFoundException { String scheme = uri.getScheme(); - if (SCHEME_ANDROID_RESOURCE.equals(scheme)) { - // Note: left here to avoid breaking compatibility. May be removed - // with sufficient testing. - OpenResourceIdResult r = getResourceId(uri); + if (SCHEME_CONTENT.equals(scheme)) { + ParcelFileDescriptor fd = openFileDescriptor(uri, "r"); + return fd != null ? new ParcelFileDescriptor.AutoCloseInputStream(fd) : null; + } else if (SCHEME_ANDROID_RESOURCE.equals(scheme)) { + String authority = uri.getAuthority(); + Resources r; + if (TextUtils.isEmpty(authority)) { + throw new FileNotFoundException("No authority: " + uri); + } else { + try { + r = mContext.getPackageManager().getResourcesForApplication(authority); + } catch (NameNotFoundException ex) { + throw new FileNotFoundException("No package found for authority: " + uri); + } + } + List<String> path = uri.getPathSegments(); + if (path == null) { + throw new FileNotFoundException("No path: " + uri); + } + int len = path.size(); + int id; + if (len == 1) { + try { + id = Integer.parseInt(path.get(0)); + } catch (NumberFormatException e) { + throw new FileNotFoundException("Single path segment is not a resource ID: " + uri); + } + } else if (len == 2) { + id = r.getIdentifier(path.get(1), path.get(0), authority); + } else { + throw new FileNotFoundException("More than two path segments: " + uri); + } + if (id == 0) { + throw new FileNotFoundException("No resource found for: " + uri); + } try { - InputStream stream = r.r.openRawResource(r.id); + InputStream stream = r.openRawResource(id); return stream; } catch (Resources.NotFoundException ex) { - throw new FileNotFoundException("Resource does not exist: " + uri); + throw new FileNotFoundException("Resource ID does not exist: " + uri); } } else if (SCHEME_FILE.equals(scheme)) { - // Note: left here to avoid breaking compatibility. May be removed - // with sufficient testing. return new FileInputStream(uri.getPath()); } else { - AssetFileDescriptor fd = openAssetFileDescriptor(uri, "r"); - try { - return fd != null ? fd.createInputStream() : null; - } catch (IOException e) { - throw new FileNotFoundException("Unable to create stream"); - } + throw new FileNotFoundException("Unknown scheme: " + uri); } } /** - * Synonym for {@link #openOutputStream(Uri, String) - * openOutputStream(uri, "w")}. - * @throws FileNotFoundException if the provided URI could not be opened. - */ - public final OutputStream openOutputStream(Uri uri) - throws FileNotFoundException { - return openOutputStream(uri, "w"); - } - - /** * Open a stream on to the content associated with a content URI. If there * is no data associated with the URI, FileNotFoundException is thrown. * * <h5>Accepts the following URI schemes:</h5> * <ul> * <li>content ({@link #SCHEME_CONTENT})</li> - * <li>file ({@link #SCHEME_FILE})</li> * </ul> * - * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information - * on these schemes. - * - * @param uri The desired URI. - * @param mode May be "w", "wa", "rw", or "rwt". + * @param uri The desired "content:" URI. * @return OutputStream - * @throws FileNotFoundException if the provided URI could not be opened. - * @see #openAssetFileDescriptor(Uri, String) */ - public final OutputStream openOutputStream(Uri uri, String mode) + public final OutputStream openOutputStream(Uri uri) throws FileNotFoundException { - AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode); - try { - return fd != null ? fd.createOutputStream() : null; - } catch (IOException e) { - throw new FileNotFoundException("Unable to create stream"); + String scheme = uri.getScheme(); + if (SCHEME_CONTENT.equals(scheme)) { + ParcelFileDescriptor fd = openFileDescriptor(uri, "rw"); + return fd != null + ? new ParcelFileDescriptor.AutoCloseOutputStream(fd) : null; + } else { + throw new FileNotFoundException("Unknown scheme: " + uri); } } /** * Open a raw file descriptor to access data under a "content:" URI. This - * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the - * underlying {@link ContentProvider#openFile} - * ContentProvider.openFile()} method, so will <em>not</em> work with - * providers that return sub-sections of files. If at all possible, - * you should use {@link #openAssetFileDescriptor(Uri, String)}. You - * will receive a FileNotFoundException exception if the provider returns a - * sub-section of a file. + * interacts with the underlying {@link ContentProvider#openFile} + * ContentProvider.openFile()} method of the provider associated with the + * given URI, to retrieve any file stored there. * * <h5>Accepts the following URI schemes:</h5> * <ul> * <li>content ({@link #SCHEME_CONTENT})</li> - * <li>file ({@link #SCHEME_FILE})</li> * </ul> * - * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information - * on these schemes. - * * @param uri The desired URI to open. * @param mode The file mode to use, as per {@link ContentProvider#openFile * ContentProvider.openFile}. @@ -273,189 +290,32 @@ public abstract class ContentResolver { * own this descriptor and are responsible for closing it when done. * @throws FileNotFoundException Throws FileNotFoundException of no * file exists under the URI or the mode is invalid. - * @see #openAssetFileDescriptor(Uri, String) */ public final ParcelFileDescriptor openFileDescriptor(Uri uri, String mode) throws FileNotFoundException { - AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode); - if (afd == null) { - return null; - } - - if (afd.getDeclaredLength() < 0) { - // This is a full file! - return afd.getParcelFileDescriptor(); + IContentProvider provider = acquireProvider(uri); + if (provider == null) { + throw new FileNotFoundException("No content provider: " + uri); } - - // Client can't handle a sub-section of a file, so close what - // we got and bail with an exception. try { - afd.close(); - } catch (IOException e) { - } - - throw new FileNotFoundException("Not a whole file"); - } - - /** - * Open a raw file descriptor to access data under a "content:" URI. This - * interacts with the underlying {@link ContentProvider#openAssetFile} - * ContentProvider.openAssetFile()} method of the provider associated with the - * given URI, to retrieve any file stored there. - * - * <h5>Accepts the following URI schemes:</h5> - * <ul> - * <li>content ({@link #SCHEME_CONTENT})</li> - * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li> - * <li>file ({@link #SCHEME_FILE})</li> - * </ul> - * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5> - * <p> - * A Uri object can be used to reference a resource in an APK file. The - * Uri should be one of the following formats: - * <ul> - * <li><code>android.resource://package_name/id_number</code><br/> - * <code>package_name</code> is your package name as listed in your AndroidManifest.xml. - * For example <code>com.example.myapp</code><br/> - * <code>id_number</code> is the int form of the ID.<br/> - * The easiest way to construct this form is - * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre> - * </li> - * <li><code>android.resource://package_name/type/name</code><br/> - * <code>package_name</code> is your package name as listed in your AndroidManifest.xml. - * For example <code>com.example.myapp</code><br/> - * <code>type</code> is the string form of the resource type. For example, <code>raw</code> - * or <code>drawable</code>. - * <code>name</code> is the string form of the resource name. That is, whatever the file - * name was in your res directory, without the type extension. - * The easiest way to construct this form is - * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre> - * </li> - * </ul> - * - * @param uri The desired URI to open. - * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile - * ContentProvider.openAssetFile}. - * @return Returns a new ParcelFileDescriptor pointing to the file. You - * own this descriptor and are responsible for closing it when done. - * @throws FileNotFoundException Throws FileNotFoundException of no - * file exists under the URI or the mode is invalid. - */ - public final AssetFileDescriptor openAssetFileDescriptor(Uri uri, - String mode) throws FileNotFoundException { - String scheme = uri.getScheme(); - if (SCHEME_ANDROID_RESOURCE.equals(scheme)) { - if (!"r".equals(mode)) { - throw new FileNotFoundException("Can't write resources: " + uri); - } - OpenResourceIdResult r = getResourceId(uri); - try { - return r.r.openRawResourceFd(r.id); - } catch (Resources.NotFoundException ex) { - throw new FileNotFoundException("Resource does not exist: " + uri); - } - } else if (SCHEME_FILE.equals(scheme)) { - ParcelFileDescriptor pfd = ParcelFileDescriptor.open( - new File(uri.getPath()), modeToMode(uri, mode)); - return new AssetFileDescriptor(pfd, 0, -1); - } else { - IContentProvider provider = acquireProvider(uri); - if (provider == null) { - throw new FileNotFoundException("No content provider: " + uri); - } - try { - AssetFileDescriptor fd = provider.openAssetFile(uri, mode); - if(fd == null) { - releaseProvider(provider); - return null; - } - ParcelFileDescriptor pfd = new ParcelFileDescriptorInner( - fd.getParcelFileDescriptor(), provider); - return new AssetFileDescriptor(pfd, fd.getStartOffset(), - fd.getDeclaredLength()); - } catch (RemoteException e) { - releaseProvider(provider); - throw new FileNotFoundException("Dead content provider: " + uri); - } catch (FileNotFoundException e) { + ParcelFileDescriptor fd = provider.openFile(uri, mode); + if(fd == null) { releaseProvider(provider); - throw e; - } catch (RuntimeException e) { - releaseProvider(provider); - throw e; + return null; } + return new ParcelFileDescriptorInner(fd, provider); + } catch (RemoteException e) { + releaseProvider(provider); + throw new FileNotFoundException("Dead content provider: " + uri); + } catch (FileNotFoundException e) { + releaseProvider(provider); + throw e; + } catch (RuntimeException e) { + releaseProvider(provider); + throw e; } } - class OpenResourceIdResult { - Resources r; - int id; - } - - OpenResourceIdResult getResourceId(Uri uri) throws FileNotFoundException { - String authority = uri.getAuthority(); - Resources r; - if (TextUtils.isEmpty(authority)) { - throw new FileNotFoundException("No authority: " + uri); - } else { - try { - r = mContext.getPackageManager().getResourcesForApplication(authority); - } catch (NameNotFoundException ex) { - throw new FileNotFoundException("No package found for authority: " + uri); - } - } - List<String> path = uri.getPathSegments(); - if (path == null) { - throw new FileNotFoundException("No path: " + uri); - } - int len = path.size(); - int id; - if (len == 1) { - try { - id = Integer.parseInt(path.get(0)); - } catch (NumberFormatException e) { - throw new FileNotFoundException("Single path segment is not a resource ID: " + uri); - } - } else if (len == 2) { - id = r.getIdentifier(path.get(1), path.get(0), authority); - } else { - throw new FileNotFoundException("More than two path segments: " + uri); - } - if (id == 0) { - throw new FileNotFoundException("No resource found for: " + uri); - } - OpenResourceIdResult res = new OpenResourceIdResult(); - res.r = r; - res.id = id; - return res; - } - - /** @hide */ - static public int modeToMode(Uri uri, String mode) throws FileNotFoundException { - int modeBits; - if ("r".equals(mode)) { - modeBits = ParcelFileDescriptor.MODE_READ_ONLY; - } else if ("w".equals(mode) || "wt".equals(mode)) { - modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY - | ParcelFileDescriptor.MODE_CREATE - | ParcelFileDescriptor.MODE_TRUNCATE; - } else if ("wa".equals(mode)) { - modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY - | ParcelFileDescriptor.MODE_CREATE - | ParcelFileDescriptor.MODE_APPEND; - } else if ("rw".equals(mode)) { - modeBits = ParcelFileDescriptor.MODE_READ_WRITE - | ParcelFileDescriptor.MODE_CREATE; - } else if ("rwt".equals(mode)) { - modeBits = ParcelFileDescriptor.MODE_READ_WRITE - | ParcelFileDescriptor.MODE_CREATE - | ParcelFileDescriptor.MODE_TRUNCATE; - } else { - throw new FileNotFoundException("Bad mode for " + uri + ": " - + mode); - } - return modeBits; - } - /** * Inserts a row into a table at the given URL. * diff --git a/core/java/android/content/ContentServiceNative.java b/core/java/android/content/ContentServiceNative.java index 364f9ee..f050501 100644 --- a/core/java/android/content/ContentServiceNative.java +++ b/core/java/android/content/ContentServiceNative.java @@ -75,13 +75,6 @@ abstract class ContentServiceNative extends Binder implements IContentService { try { switch (code) { - case 5038: { - data.readString(); // ignore the interface token that service generated - Uri uri = Uri.parse(data.readString()); - notifyChange(uri, null, false, false); - return true; - } - case REGISTER_CONTENT_OBSERVER_TRANSACTION: { Uri uri = Uri.CREATOR.createFromParcel(data); boolean notifyForDescendents = data.readInt() != 0; diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java index 0606956..a6ef46f 100644 --- a/core/java/android/content/IContentProvider.java +++ b/core/java/android/content/IContentProvider.java @@ -16,7 +16,6 @@ package android.content; -import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.database.CursorWindow; import android.database.IBulkCursor; @@ -53,8 +52,6 @@ public interface IContentProvider extends IInterface { String[] selectionArgs) throws RemoteException; public ParcelFileDescriptor openFile(Uri url, String mode) throws RemoteException, FileNotFoundException; - public AssetFileDescriptor openAssetFile(Uri url, String mode) - throws RemoteException, FileNotFoundException; public ISyncAdapter getSyncAdapter() throws RemoteException; /* IPC constants */ @@ -68,5 +65,4 @@ public interface IContentProvider extends IInterface { static final int GET_SYNC_ADAPTER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 10; static final int BULK_INSERT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 12; static final int OPEN_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 13; - static final int OPEN_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 14; } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e1c1f64..c1c3b49 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -522,7 +522,6 @@ import java.util.Set; * <li> {@link #CATEGORY_ALTERNATIVE} * <li> {@link #CATEGORY_SELECTED_ALTERNATIVE} * <li> {@link #CATEGORY_LAUNCHER} - * <li> {@link #CATEGORY_INFO} * <li> {@link #CATEGORY_HOME} * <li> {@link #CATEGORY_PREFERENCE} * <li> {@link #CATEGORY_GADGET} @@ -1547,13 +1546,6 @@ public class Intent implements Parcelable { @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER"; /** - * Provides information about the package it is in; typically used if - * a package does not contain a {@link #CATEGORY_LAUNCHER} to provide - * a front-door to the user without having to be shown in the all apps list. - */ - @SdkConstant(SdkConstantType.INTENT_CATEGORY) - public static final String CATEGORY_INFO = "android.intent.category.INFO"; - /** * This is the home activity, that is the first activity that is displayed * when the device boots. */ diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 7287d9c..698f27f 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -480,26 +480,6 @@ public abstract class PackageManager { throws NameNotFoundException; /** - * Return a "good" intent to launch a front-door activity in a package, - * for use for example to implement an "open" button when browsing through - * packages. The current implementation will look first for a main - * activity in the category {@link Intent#CATEGORY_INFO}, next for a - * main activity in the category {@link Intent#CATEGORY_LAUNCHER}, or return - * null if neither are found. - * - * <p>Throws {@link NameNotFoundException} if a package with the given - * name can not be found on the system. - * - * @param packageName The name of the package to inspect. - * - * @return Returns either a fully-qualified Intent that can be used to - * launch the main activity in the package, or null if the package does - * not contain such an activity. - */ - public abstract Intent getLaunchIntentForPackage(String packageName) - throws NameNotFoundException; - - /** * Return an array of all of the secondary group-ids that have been * assigned to a package. * diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java index 231e3e2..4a073f7 100644 --- a/core/java/android/content/res/AssetFileDescriptor.java +++ b/core/java/android/content/res/AssetFileDescriptor.java @@ -16,13 +16,9 @@ package android.content.res; -import android.os.Parcel; import android.os.ParcelFileDescriptor; -import android.os.Parcelable; import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; /** @@ -30,32 +26,16 @@ import java.io.IOException; * opened FileDescriptor that can be used to read the data, as well as the * offset and length of that entry's data in the file. */ -public class AssetFileDescriptor implements Parcelable { - /** - * Length used with {@link #AssetFileDescriptor(ParcelFileDescriptor, long, long)} - * and {@link #getDeclaredLength} when a length has not been declared. This means - * the data extends to the end of the file. - */ - public static final long UNKNOWN_LENGTH = -1; - +public class AssetFileDescriptor { private final ParcelFileDescriptor mFd; private final long mStartOffset; private final long mLength; /** * Create a new AssetFileDescriptor from the given values. - * @param fd The underlying file descriptor. - * @param startOffset The location within the file that the asset starts. - * This must be 0 if length is UNKNOWN_LENGTH. - * @param length The number of bytes of the asset, or - * {@link #UNKNOWN_LENGTH if it extends to the end of the file. */ public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, long length) { - if (length < 0 && startOffset != 0) { - throw new IllegalArgumentException( - "startOffset must be 0 when using UNKNOWN_LENGTH"); - } mFd = fd; mStartOffset = startOffset; mLength = length; @@ -86,33 +66,9 @@ public class AssetFileDescriptor implements Parcelable { } /** - * Returns the total number of bytes of this asset entry's data. May be - * {@link #UNKNOWN_LENGTH} if the asset extends to the end of the file. - * If the AssetFileDescriptor was constructed with {@link #UNKNOWN_LENGTH}, - * this will use {@link ParcelFileDescriptor#getStatSize() - * ParcelFileDescriptor.getStatSize()} to find the total size of the file, - * returning that number if found or {@link #UNKNOWN_LENGTH} if it could - * not be determined. - * - * @see #getDeclaredLength() + * Returns the total number of bytes of this asset entry's data. */ public long getLength() { - if (mLength >= 0) { - return mLength; - } - long len = mFd.getStatSize(); - return len >= 0 ? len : UNKNOWN_LENGTH; - } - - /** - * Return the actual number of bytes that were declared when the - * AssetFileDescriptor was constructed. Will be - * {@link #UNKNOWN_LENGTH} if the length was not declared, meaning data - * should be read to the end of the file. - * - * @see #getDeclaredLength() - */ - public long getDeclaredLength() { return mLength; } @@ -122,227 +78,4 @@ public class AssetFileDescriptor implements Parcelable { public void close() throws IOException { mFd.close(); } - - /** - * Create and return a new auto-close input stream for this asset. This - * will either return a full asset {@link AutoCloseInputStream}, or - * an underlying {@link ParcelFileDescriptor.AutoCloseInputStream - * ParcelFileDescriptor.AutoCloseInputStream} depending on whether the - * the object represents a complete file or sub-section of a file. You - * should only call this once for a particular asset. - */ - public FileInputStream createInputStream() throws IOException { - if (mLength < 0) { - return new ParcelFileDescriptor.AutoCloseInputStream(mFd); - } - return new AutoCloseInputStream(this); - } - - /** - * Create and return a new auto-close output stream for this asset. This - * will either return a full asset {@link AutoCloseOutputStream}, or - * an underlying {@link ParcelFileDescriptor.AutoCloseOutputStream - * ParcelFileDescriptor.AutoCloseOutputStream} depending on whether the - * the object represents a complete file or sub-section of a file. You - * should only call this once for a particular asset. - */ - public FileOutputStream createOutputStream() throws IOException { - if (mLength < 0) { - return new ParcelFileDescriptor.AutoCloseOutputStream(mFd); - } - return new AutoCloseOutputStream(this); - } - - @Override - public String toString() { - return "{AssetFileDescriptor: " + mFd - + " start=" + mStartOffset + " len=" + mLength + "}"; - } - - /** - * An InputStream you can create on a ParcelFileDescriptor, which will - * take care of calling {@link ParcelFileDescriptor#close - * ParcelFileDescritor.close()} for you when the stream is closed. - */ - public static class AutoCloseInputStream - extends ParcelFileDescriptor.AutoCloseInputStream { - private long mRemaining; - - public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException { - super(fd.getParcelFileDescriptor()); - super.skip(fd.getStartOffset()); - mRemaining = (int)fd.getLength(); - } - - @Override - public int available() throws IOException { - return mRemaining >= 0 - ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff) - : super.available(); - } - - @Override - public int read() throws IOException { - if (mRemaining >= 0) { - if (mRemaining == 0) return -1; - int res = super.read(); - if (res >= 0) mRemaining--; - return res; - } - - return super.read(); - } - - @Override - public int read(byte[] buffer, int offset, int count) throws IOException { - if (mRemaining >= 0) { - if (mRemaining == 0) return -1; - if (count > mRemaining) count = (int)mRemaining; - int res = super.read(buffer, offset, count); - if (res >= 0) mRemaining -= res; - return res; - } - - return super.read(buffer, offset, count); - } - - @Override - public int read(byte[] buffer) throws IOException { - if (mRemaining >= 0) { - if (mRemaining == 0) return -1; - int count = buffer.length; - if (count > mRemaining) count = (int)mRemaining; - int res = super.read(buffer, 0, count); - if (res >= 0) mRemaining -= res; - return res; - } - - return super.read(buffer); - } - - @Override - public long skip(long count) throws IOException { - if (mRemaining >= 0) { - if (mRemaining == 0) return -1; - if (count > mRemaining) count = mRemaining; - long res = super.skip(count); - if (res >= 0) mRemaining -= res; - return res; - } - - // TODO Auto-generated method stub - return super.skip(count); - } - - @Override - public void mark(int readlimit) { - if (mRemaining >= 0) { - // Not supported. - return; - } - super.mark(readlimit); - } - - @Override - public boolean markSupported() { - if (mRemaining >= 0) { - return false; - } - return super.markSupported(); - } - - @Override - public synchronized void reset() throws IOException { - if (mRemaining >= 0) { - // Not supported. - return; - } - super.reset(); - } - } - - /** - * An OutputStream you can create on a ParcelFileDescriptor, which will - * take care of calling {@link ParcelFileDescriptor#close - * ParcelFileDescritor.close()} for you when the stream is closed. - */ - public static class AutoCloseOutputStream - extends ParcelFileDescriptor.AutoCloseOutputStream { - private long mRemaining; - - public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException { - super(fd.getParcelFileDescriptor()); - if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) { - throw new IOException("Unable to seek"); - } - mRemaining = (int)fd.getLength(); - } - - @Override - public void write(byte[] buffer, int offset, int count) throws IOException { - if (mRemaining >= 0) { - if (mRemaining == 0) return; - if (count > mRemaining) count = (int)mRemaining; - super.write(buffer, offset, count); - mRemaining -= count; - return; - } - - super.write(buffer, offset, count); - } - - @Override - public void write(byte[] buffer) throws IOException { - if (mRemaining >= 0) { - if (mRemaining == 0) return; - int count = buffer.length; - if (count > mRemaining) count = (int)mRemaining; - super.write(buffer); - mRemaining -= count; - return; - } - - super.write(buffer); - } - - @Override - public void write(int oneByte) throws IOException { - if (mRemaining >= 0) { - if (mRemaining == 0) return; - super.write(oneByte); - mRemaining--; - return; - } - - super.write(oneByte); - } - } - - - /* Parcelable interface */ - public int describeContents() { - return mFd.describeContents(); - } - - public void writeToParcel(Parcel out, int flags) { - mFd.writeToParcel(out, flags); - out.writeLong(mStartOffset); - out.writeLong(mLength); - } - - AssetFileDescriptor(Parcel src) { - mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src); - mStartOffset = src.readLong(); - mLength = src.readLong(); - } - - public static final Parcelable.Creator<AssetFileDescriptor> CREATOR - = new Parcelable.Creator<AssetFileDescriptor>() { - public AssetFileDescriptor createFromParcel(Parcel in) { - return new AssetFileDescriptor(in); - } - public AssetFileDescriptor[] newArray(int size) { - return new AssetFileDescriptor[size]; - } - }; } diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java index 0f3f270..17cb687 100644 --- a/core/java/android/content/res/ColorStateList.java +++ b/core/java/android/content/res/ColorStateList.java @@ -16,6 +16,8 @@ package android.content.res; +import com.google.android.collect.Lists; + import com.android.internal.util.ArrayUtils; import org.xmlpull.v1.XmlPullParser; @@ -111,8 +113,7 @@ public class ColorStateList implements Parcelable { * Create a ColorStateList from an XML document, given a set of {@link Resources}. */ public static ColorStateList createFromXml(Resources r, XmlPullParser parser) - throws XmlPullParserException, IOException { - + throws XmlPullParserException, IOException { AttributeSet attrs = Xml.asAttributeSet(parser); int type; @@ -124,16 +125,19 @@ public class ColorStateList implements Parcelable { throw new XmlPullParserException("No start tag found"); } - return createFromXmlInner(r, parser, attrs); + final ColorStateList colorStateList = createFromXmlInner(r, parser, attrs); + + return colorStateList; } /* Create from inside an XML document. Called on a parser positioned at * a tag in an XML document, tries to create a ColorStateList from that tag. * Returns null if the tag is not a valid ColorStateList. */ - private static ColorStateList createFromXmlInner(Resources r, XmlPullParser parser, - AttributeSet attrs) throws XmlPullParserException, IOException { - + private static ColorStateList createFromXmlInner(Resources r, + XmlPullParser parser, + AttributeSet attrs) + throws XmlPullParserException, IOException { ColorStateList colorStateList; final String name = parser.getName(); @@ -142,7 +146,8 @@ public class ColorStateList implements Parcelable { colorStateList = new ColorStateList(); } else { throw new XmlPullParserException( - parser.getPositionDescription() + ": invalid drawable tag " + name); + parser.getPositionDescription() + ": invalid drawable tag " + + name); } colorStateList.inflate(r, parser, attrs); diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 1a963f6..5a0daea 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -46,7 +46,6 @@ public class Resources { static final String TAG = "Resources"; private static final boolean DEBUG_LOAD = false; private static final boolean DEBUG_CONFIG = false; - private static final boolean TRACE_FOR_PRELOAD = false; private static final int sSdkVersion = SystemProperties.getInt( "ro.build.version.sdk", 0); @@ -58,8 +57,6 @@ public class Resources { // single-threaded, and after that these are immutable. private static final SparseArray<Drawable.ConstantState> mPreloadedDrawables = new SparseArray<Drawable.ConstantState>(); - private static final SparseArray<ColorStateList> mPreloadedColorStateLists - = new SparseArray<ColorStateList>(); private static boolean mPreloaded; /*package*/ final TypedValue mTmpValue = new TypedValue(); @@ -81,7 +78,7 @@ public class Resources { private final Configuration mConfiguration = new Configuration(); /*package*/ final DisplayMetrics mMetrics = new DisplayMetrics(); PluralRules mPluralRule; - + /** * This exception is thrown by the resource APIs when a requested resource * can not be found. @@ -93,7 +90,7 @@ public class Resources { public NotFoundException(String name) { super(name); } - } + }; /** * Create a new Resources object on top of an existing set of assets in an @@ -1232,9 +1229,7 @@ public class Resources { width = mMetrics.widthPixels; height = mMetrics.heightPixels; } else { - //noinspection SuspiciousNameCombination width = mMetrics.heightPixels; - //noinspection SuspiciousNameCombination height = mMetrics.widthPixels; } int keyboardHidden = mConfiguration.keyboardHidden; @@ -1347,7 +1342,6 @@ public class Resources { try { return Integer.parseInt(name); } catch (Exception e) { - // Ignore } return mAssets.getResourceIdentifier(name, defType, defPackage); } @@ -1581,18 +1575,21 @@ public class Resources { /*package*/ Drawable loadDrawable(TypedValue value, int id) throws NotFoundException { - - if (TRACE_FOR_PRELOAD) { - // Log only framework resources - if ((id >>> 24) == 0x1) { - final String name = getResourceName(id); - if (name != null) android.util.Log.d("PreloadDrawable", name); - } + if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT + && value.type <= TypedValue.TYPE_LAST_COLOR_INT) { + // Should we be caching these? If we use constant colors much + // at all, most likely... + //System.out.println("Creating drawable for color: #" + + // Integer.toHexString(value.data)); + Drawable dr = new ColorDrawable(value.data); + dr.setChangingConfigurations(value.changingConfigurations); + return dr; } - final int key = (value.assetCookie << 24) | value.data; + final int key = (value.assetCookie<<24)|value.data; Drawable dr = getCachedDrawable(key); - + //System.out.println("Cached drawable @ #" + + // Integer.toHexString(key.intValue()) + ": " + dr); if (dr != null) { return dr; } @@ -1600,52 +1597,46 @@ public class Resources { Drawable.ConstantState cs = mPreloadedDrawables.get(key); if (cs != null) { dr = cs.newDrawable(); + } else { - if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && - value.type <= TypedValue.TYPE_LAST_COLOR_INT) { - dr = new ColorDrawable(value.data); + if (value.string == null) { + throw new NotFoundException( + "Resource is not a Drawable (color or path): " + value); } - - if (dr == null) { - if (value.string == null) { - throw new NotFoundException( - "Resource is not a Drawable (color or path): " + value); + + String file = value.string.toString(); + + if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie " + + value.assetCookie + ": " + file); + + if (file.endsWith(".xml")) { + try { + XmlResourceParser rp = loadXmlResourceParser( + file, id, value.assetCookie, "drawable"); + dr = Drawable.createFromXml(this, rp); + rp.close(); + } catch (Exception e) { + NotFoundException rnf = new NotFoundException( + "File " + file + " from drawable resource ID #0x" + + Integer.toHexString(id)); + rnf.initCause(e); + throw rnf; } - - String file = value.string.toString(); - - if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie " - + value.assetCookie + ": " + file); - - if (file.endsWith(".xml")) { - try { - XmlResourceParser rp = loadXmlResourceParser( - file, id, value.assetCookie, "drawable"); - dr = Drawable.createFromXml(this, rp); - rp.close(); - } catch (Exception e) { - NotFoundException rnf = new NotFoundException( - "File " + file + " from drawable resource ID #0x" - + Integer.toHexString(id)); - rnf.initCause(e); - throw rnf; - } - - } else { - try { - InputStream is = mAssets.openNonAsset( - value.assetCookie, file, AssetManager.ACCESS_BUFFER); - // System.out.println("Opened file " + file + ": " + is); - dr = Drawable.createFromResourceStream(this, value, is, file); - is.close(); - // System.out.println("Created stream: " + dr); - } catch (Exception e) { - NotFoundException rnf = new NotFoundException( - "File " + file + " from drawable resource ID #0x" - + Integer.toHexString(id)); - rnf.initCause(e); - throw rnf; - } + + } else { + try { + InputStream is = mAssets.openNonAsset( + value.assetCookie, file, AssetManager.ACCESS_BUFFER); + // System.out.println("Opened file " + file + ": " + is); + dr = Drawable.createFromResourceStream(this, value, is, file); + is.close(); + // System.out.println("Created stream: " + dr); + } catch (Exception e) { + NotFoundException rnf = new NotFoundException( + "File " + file + " from drawable resource ID #0x" + + Integer.toHexString(id)); + rnf.initCause(e); + throw rnf; } } } @@ -1656,13 +1647,13 @@ public class Resources { if (cs != null) { if (mPreloading) { mPreloadedDrawables.put(key, cs); - } else { - synchronized (mTmpValue) { - //Log.i(TAG, "Saving cached drawable @ #" + - // Integer.toHexString(key.intValue()) - // + " in " + this + ": " + cs); - mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); - } + } + synchronized (mTmpValue) { + //Log.i(TAG, "Saving cached drawable @ #" + + // Integer.toHexString(key.intValue()) + // + " in " + this + ": " + cs); + mDrawableCache.put( + key, new WeakReference<Drawable.ConstantState>(cs)); } } } @@ -1670,7 +1661,7 @@ public class Resources { return dr; } - private Drawable getCachedDrawable(int key) { + private final Drawable getCachedDrawable(int key) { synchronized (mTmpValue) { WeakReference<Drawable.ConstantState> wr = mDrawableCache.get(key); if (wr != null) { // we have the key @@ -1691,40 +1682,13 @@ public class Resources { /*package*/ ColorStateList loadColorStateList(TypedValue value, int id) throws NotFoundException { - if (TRACE_FOR_PRELOAD) { - // Log only framework resources - if ((id >>> 24) == 0x1) { - final String name = getResourceName(id); - if (name != null) android.util.Log.d("PreloadColorStateList", name); - } - } - - final int key = (value.assetCookie << 24) | value.data; - - ColorStateList csl; - - if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && - value.type <= TypedValue.TYPE_LAST_COLOR_INT) { - - csl = mPreloadedColorStateLists.get(key); - if (csl != null) { - return csl; - } - - csl = ColorStateList.valueOf(value.data); - if (mPreloading) { - mPreloadedColorStateLists.put(key, csl); - } - - return csl; + if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT + && value.type <= TypedValue.TYPE_LAST_COLOR_INT) { + return ColorStateList.valueOf(value.data); } - csl = getCachedColorStateList(key); - if (csl != null) { - return csl; - } - - csl = mPreloadedColorStateLists.get(key); + final int key = (value.assetCookie<<24)|value.data; + ColorStateList csl = getCachedColorStateList(key); if (csl != null) { return csl; } @@ -1756,16 +1720,12 @@ public class Resources { } if (csl != null) { - if (mPreloading) { - mPreloadedColorStateLists.put(key, csl); - } else { - synchronized (mTmpValue) { - //Log.i(TAG, "Saving cached color state list @ #" + - // Integer.toHexString(key.intValue()) - // + " in " + this + ": " + csl); - mColorStateListCache.put( - key, new WeakReference<ColorStateList>(csl)); - } + synchronized (mTmpValue) { + //Log.i(TAG, "Saving cached color state list @ #" + + // Integer.toHexString(key.intValue()) + // + " in " + this + ": " + csl); + mColorStateListCache.put( + key, new WeakReference<ColorStateList>(csl)); } } diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java index e684cb8..3df7708 100644 --- a/core/java/android/content/res/StringBlock.java +++ b/core/java/android/content/res/StringBlock.java @@ -141,8 +141,6 @@ final class StringBlock { int type = style[i]; if (localLOGV) Log.v(TAG, "Applying style span id=" + type + ", start=" + style[i+1] + ", end=" + style[i+2]); - - if (type == ids.boldId) { buffer.setSpan(new StyleSpan(Typeface.BOLD), style[i+1], style[i+2]+1, @@ -180,8 +178,9 @@ final class StringBlock { style[i+1], style[i+2]+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } else if (type == ids.listItemId) { - addParagraphSpan(buffer, new BulletSpan(10), - style[i+1], style[i+2]+1); + buffer.setSpan(new BulletSpan(10), + style[i+1], style[i+2]+1, + Spannable.SPAN_PARAGRAPH); } else if (type == ids.marqueeId) { buffer.setSpan(TextUtils.TruncateAt.MARQUEE, style[i+1], style[i+2]+1, @@ -195,8 +194,9 @@ final class StringBlock { sub = subtag(tag, ";height="); if (sub != null) { int size = Integer.parseInt(sub); - addParagraphSpan(buffer, new Height(size), - style[i+1], style[i+2]+1); + buffer.setSpan(new Height(size), + style[i+1], style[i+2]+1, + Spannable.SPAN_PARAGRAPH); } sub = subtag(tag, ";size="); @@ -231,28 +231,6 @@ final class StringBlock { style[i+1], style[i+2]+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } - } else if (tag.startsWith("annotation;")) { - int len = tag.length(); - int next; - - for (int t = tag.indexOf(';'); t < len; t = next) { - int eq = tag.indexOf('=', t); - if (eq < 0) { - break; - } - - next = tag.indexOf(';', eq); - if (next < 0) { - next = len; - } - - String key = tag.substring(t + 1, eq); - String value = tag.substring(eq + 1, next); - - buffer.setSpan(new Annotation(key, value), - style[i+1], style[i+2]+1, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } } } @@ -261,34 +239,6 @@ final class StringBlock { return new SpannedString(buffer); } - /** - * If a translator has messed up the edges of paragraph-level markup, - * fix it to actually cover the entire paragraph that it is attached to - * instead of just whatever range they put it on. - */ - private static void addParagraphSpan(Spannable buffer, Object what, - int start, int end) { - int len = buffer.length(); - - if (start != 0 && start != len && buffer.charAt(start - 1) != '\n') { - for (start--; start > 0; start--) { - if (buffer.charAt(start - 1) == '\n') { - break; - } - } - } - - if (end != 0 && end != len && buffer.charAt(end - 1) != '\n') { - for (end++; end < len; end++) { - if (buffer.charAt(end - 1) == '\n') { - break; - } - } - } - - buffer.setSpan(what, start, end, Spannable.SPAN_PARAGRAPH); - } - private static String subtag(String full, String attribute) { int start = full.indexOf(attribute); if (start < 0) { diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index 3a32c03..82a57dd 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -438,34 +438,6 @@ public class TypedArray { throw new RuntimeException(getPositionDescription() + ": You must supply a " + name + " attribute."); } - - /** - * Special version of {@link #getDimensionPixelSize} for retrieving - * {@link android.view.ViewGroup}'s layout_width and layout_height - * attributes. This is only here for performance reasons; applications - * should use {@link #getDimensionPixelSize}. - * - * @param index Index of the attribute to retrieve. - * @param defValue The default value to return if this attribute is not - * default or contains the wrong type of data. - * - * @return Attribute dimension value multiplied by the appropriate - * metric and truncated to integer pixels. - */ - public int getLayoutDimension(int index, int defValue) { - index *= AssetManager.STYLE_NUM_ENTRIES; - final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; - if (type >= TypedValue.TYPE_FIRST_INT - && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; - } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index+AssetManager.STYLE_DATA], mResources.mMetrics); - } - - return defValue; - } /** * Retrieve a fractional unit attribute at <var>index</var>. diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 2af080a..87bb277 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -778,9 +778,9 @@ public class SQLiteDatabase extends SQLiteClosable { } /** - * Returns the current database page size, in bytes. + * Returns the maximum size the database may grow to. * - * @return the database page size, in bytes + * @return the new maximum database size */ public long getPageSize() { SQLiteStatement prog = null; diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java index 5889ad9..d169259 100644 --- a/core/java/android/database/sqlite/SQLiteStatement.java +++ b/core/java/android/database/sqlite/SQLiteStatement.java @@ -17,7 +17,6 @@ package android.database.sqlite; import android.os.SystemClock; -import android.util.Log; /** * A pre-compiled statement against a {@link SQLiteDatabase} that can be reused. @@ -27,10 +26,6 @@ import android.util.Log; */ public class SQLiteStatement extends SQLiteProgram { - private static final String TAG = "SQLiteStatement"; - - private final String mSql; - /** * Don't use SQLiteStatement constructor directly, please use * {@link SQLiteDatabase#compileStatement(String)} @@ -39,11 +34,6 @@ public class SQLiteStatement extends SQLiteProgram */ /* package */ SQLiteStatement(SQLiteDatabase db, String sql) { super(db, sql); - if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { - mSql = sql; - } else { - mSql = null; - } } /** @@ -60,9 +50,6 @@ public class SQLiteStatement extends SQLiteProgram acquireReference(); try { - if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { - Log.v(TAG, "execute() for [" + mSql + "]"); - } native_execute(); if (logStats) { mDatabase.logTimeStat(false /* write */, startTime, SystemClock.elapsedRealtime()); @@ -90,9 +77,6 @@ public class SQLiteStatement extends SQLiteProgram acquireReference(); try { - if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { - Log.v(TAG, "executeInsert() for [" + mSql + "]"); - } native_execute(); if (logStats) { mDatabase.logTimeStat(false /* write */, startTime, SystemClock.elapsedRealtime()); @@ -119,9 +103,6 @@ public class SQLiteStatement extends SQLiteProgram acquireReference(); try { - if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { - Log.v(TAG, "simpleQueryForLong() for [" + mSql + "]"); - } long retValue = native_1x1_long(); if (logStats) { mDatabase.logTimeStat(false /* write */, startTime, SystemClock.elapsedRealtime()); @@ -148,9 +129,6 @@ public class SQLiteStatement extends SQLiteProgram acquireReference(); try { - if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { - Log.v(TAG, "simpleQueryForString() for [" + mSql + "]"); - } String retValue = native_1x1_string(); if (logStats) { mDatabase.logTimeStat(false /* write */, startTime, SystemClock.elapsedRealtime()); diff --git a/core/java/android/gadget/GadgetHost.java b/core/java/android/gadget/GadgetHost.java index 3d88b58..31aed32 100644 --- a/core/java/android/gadget/GadgetHost.java +++ b/core/java/android/gadget/GadgetHost.java @@ -19,7 +19,6 @@ package android.gadget; import android.content.Context; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; @@ -63,11 +62,7 @@ public class GadgetHost { } } - class UpdateHandler extends Handler { - public UpdateHandler(Looper looper) { - super(looper); - } - + Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case HANDLE_UPDATE: { @@ -80,9 +75,7 @@ public class GadgetHost { } } } - } - - Handler mHandler; + }; int mHostId; Callbacks mCallbacks = new Callbacks(); @@ -91,7 +84,6 @@ public class GadgetHost { public GadgetHost(Context context, int hostId) { mContext = context; mHostId = hostId; - mHandler = new UpdateHandler(context.getMainLooper()); synchronized (sServiceLock) { if (sService == null) { IBinder b = ServiceManager.getService(Context.GADGET_SERVICE); diff --git a/core/java/android/gadget/GadgetHostView.java b/core/java/android/gadget/GadgetHostView.java index 5cbd988..a985bd4 100644 --- a/core/java/android/gadget/GadgetHostView.java +++ b/core/java/android/gadget/GadgetHostView.java @@ -18,13 +18,7 @@ package android.gadget; import android.content.Context; import android.content.pm.PackageManager; -import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.Paint; -import android.os.Handler; -import android.os.Message; -import android.os.SystemClock; import android.util.Config; import android.util.Log; import android.view.Gravity; @@ -35,23 +29,16 @@ import android.view.animation.Animation; import android.widget.FrameLayout; import android.widget.RemoteViews; import android.widget.TextView; +import android.widget.ViewAnimator; /** * Provides the glue to show gadget views. This class offers automatic animation * between updates, and will try recycling old views for each incoming * {@link RemoteViews}. */ -public class GadgetHostView extends FrameLayout { +public class GadgetHostView extends ViewAnimator implements Animation.AnimationListener { static final String TAG = "GadgetHostView"; - static final boolean LOGD = false; - static final boolean CROSSFADE = false; - - static final int VIEW_MODE_NOINIT = 0; - static final int VIEW_MODE_CONTENT = 1; - static final int VIEW_MODE_ERROR = 2; - static final int VIEW_MODE_DEFAULT = 3; - - static final int FADE_DURATION = 1000; + 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. @@ -60,17 +47,28 @@ public class GadgetHostView extends FrameLayout { return clazz.isAnnotationPresent(RemoteViews.RemoteView.class); } }; - - Context mContext; + + Context mLocalContext; int mGadgetId; GadgetProviderInfo mInfo; - View mView; - int mViewMode = VIEW_MODE_NOINIT; - int mLayoutId = -1; - long mFadeStartTime = -1; - Bitmap mOld; - Paint mOldPaint = new Paint(); + + View mActiveView = null; + View mStaleView = null; + + int mActiveLayoutId = -1; + int mStaleLayoutId = -1; + + /** + * Last set of {@link RemoteViews} applied to {@link #mActiveView} + */ + RemoteViews mActiveActions = null; + + /** + * Flag indicating that {@link #mActiveActions} has been applied to + * {@link #mStaleView}, meaning it's readyto recycle. + */ + boolean mStalePrepared = false; /** * Create a host view. Uses default fade animations. @@ -88,13 +86,27 @@ public class GadgetHostView extends FrameLayout { */ public GadgetHostView(Context context, int animationIn, int animationOut) { super(context); - mContext = context; + mLocalContext = context; + + // Prepare our default transition animations + setAnimateFirstView(true); + setInAnimation(context, animationIn); + setOutAnimation(context, animationOut); + + // Watch for animation events to prepare recycling + Animation inAnimation = getInAnimation(); + if (inAnimation != null) { + inAnimation.setAnimationListener(this); + } } /** * Set the gadget that will be displayed by this view. */ public void setGadget(int gadgetId, GadgetProviderInfo info) { + if (mInfo != null) { + // TODO: remove the old view, or whatever + } mGadgetId = gadgetId; mInfo = info; } @@ -107,141 +119,92 @@ public class GadgetHostView extends FrameLayout { return mInfo; } + public void onAnimationEnd(Animation animation) { + // When our transition animation finishes, we should try bringing our + // newly-stale view up to the current view. + if (mActiveActions != null && + mStaleLayoutId == mActiveActions.getLayoutId()) { + if (LOGD) Log.d(TAG, "after animation, layoutId matched so we're recycling old view"); + mActiveActions.reapply(mLocalContext, mStaleView); + mStalePrepared = true; + } + } + + public void onAnimationRepeat(Animation animation) { + } + + public void onAnimationStart(Animation animation) { + } + /** * 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) { - if (LOGD) Log.d(TAG, "updateGadget called mOld=" + mOld); + if (LOGD) Log.d(TAG, "updateGadget called"); boolean recycled = false; - View content = null; + View newContent = null; Exception exception = null; - // Capture the old view into a bitmap so we can do the crossfade. - if (CROSSFADE) { - if (mFadeStartTime < 0) { - if (mView != null) { - final int width = mView.getWidth(); - final int height = mView.getHeight(); - try { - mOld = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - } catch (OutOfMemoryError e) { - // we just won't do the fade - mOld = null; - } - if (mOld != null) { - //mView.drawIntoBitmap(mOld); - } - } - } + if (remoteViews == null) { + newContent = getDefaultView(); } - if (remoteViews == null) { - if (mViewMode == VIEW_MODE_DEFAULT) { - // We've already done this -- nothing to do. - return; - } - content = getDefaultView(); - mLayoutId = -1; - mViewMode = VIEW_MODE_DEFAULT; - } else { - int layoutId = remoteViews.getLayoutId(); - - // If our stale view has been prepared to match active, and the new - // layout matches, try recycling it - if (content == null && layoutId == mLayoutId) { - try { - remoteViews.reapply(mContext, mView); - content = mView; - recycled = true; - if (LOGD) Log.d(TAG, "was able to recycled existing layout"); - } catch (RuntimeException e) { - exception = e; - } - } - - // Try normal RemoteView inflation - if (content == null) { - try { - content = remoteViews.apply(mContext, this); - if (LOGD) Log.d(TAG, "had to inflate new layout"); - } catch (RuntimeException e) { - exception = e; - } + // If our stale view has been prepared to match active, and the new + // layout matches, try recycling it + if (newContent == null && mStalePrepared && + remoteViews.getLayoutId() == mStaleLayoutId) { + try { + remoteViews.reapply(mLocalContext, mStaleView); + newContent = mStaleView; + recycled = true; + if (LOGD) Log.d(TAG, "was able to recycled existing layout"); + } catch (RuntimeException e) { + exception = e; } - - mLayoutId = layoutId; - mViewMode = VIEW_MODE_CONTENT; } - if (content == null) { - if (mViewMode == VIEW_MODE_ERROR) { - // We've already done this -- nothing to do. - return ; + // Try normal RemoteView inflation + if (newContent == null) { + try { + newContent = remoteViews.apply(mLocalContext, this); + if (LOGD) Log.d(TAG, "had to inflate new layout"); + } catch (RuntimeException e) { + exception = e; } - Log.w(TAG, "updateGadget couldn't find any view, using error view", exception); - content = getErrorView(); - mViewMode = VIEW_MODE_ERROR; } - if (!recycled) { - prepareView(content); - addView(content); + if (exception != null && LOGD) { + Log.w(TAG, "Error inflating gadget " + getGadgetInfo(), exception); } - - if (mView != content) { - removeView(mView); - mView = content; + + if (newContent == null) { + // TODO: Should we throw an exception here for the host activity to catch? + // Maybe we should show a generic error widget. + if (LOGD) Log.d(TAG, "updateGadget couldn't find any view, so inflating error"); + newContent = getErrorView(); } - - if (CROSSFADE) { - if (mFadeStartTime < 0) { - // if there is already an animation in progress, don't do anything -- - // the new view will pop in on top of the old one during the cross fade, - // and that looks okay. - mFadeStartTime = SystemClock.uptimeMillis(); - invalidate(); - } + + if (!recycled) { + prepareView(newContent); + addView(newContent); } - } - - protected boolean drawChild(Canvas canvas, View child, long drawingTime) { - if (CROSSFADE) { - int alpha; - int l = child.getLeft(); - int t = child.getTop(); - if (mFadeStartTime > 0) { - alpha = (int)(((drawingTime-mFadeStartTime)*255)/FADE_DURATION); - if (alpha > 255) { - alpha = 255; - } - Log.d(TAG, "drawChild alpha=" + alpha + " l=" + l + " t=" + t - + " w=" + child.getWidth()); - if (alpha != 255 && mOld != null) { - mOldPaint.setAlpha(255-alpha); - //canvas.drawBitmap(mOld, l, t, mOldPaint); - } - } else { - alpha = 255; - } - int restoreTo = canvas.saveLayerAlpha(l, t, child.getWidth(), child.getHeight(), alpha, - Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); - boolean rv = super.drawChild(canvas, child, drawingTime); - canvas.restoreToCount(restoreTo); - if (alpha < 255) { - invalidate(); - } else { - mFadeStartTime = -1; - if (mOld != null) { - mOld.recycle(); - mOld = null; - } - } - return rv; - } else { - return super.drawChild(canvas, child, drawingTime); + + showNext(); + + if (!recycled) { + removeView(mStaleView); } + + mStalePrepared = false; + mActiveActions = remoteViews; + + mStaleView = mActiveView; + mActiveView = newContent; + + mStaleLayoutId = mActiveLayoutId; + mActiveLayoutId = (remoteViews == null) ? -1 : remoteViews.getLayoutId(); } /** @@ -271,7 +234,7 @@ public class GadgetHostView extends FrameLayout { try { if (mInfo != null) { - Context theirContext = mContext.createPackageContext( + Context theirContext = mLocalContext.createPackageContext( mInfo.provider.getPackageName(), 0 /* no flags */); LayoutInflater inflater = (LayoutInflater) theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); @@ -303,9 +266,9 @@ public class GadgetHostView extends FrameLayout { * Inflate and return a view that represents an error state. */ protected View getErrorView() { - TextView tv = new TextView(mContext); - tv.setText(com.android.internal.R.string.gadget_host_error_inflating); - // TODO: get this color from somewhere. + TextView tv = new TextView(mLocalContext); + // 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/GadgetManager.java b/core/java/android/gadget/GadgetManager.java index d2c4055..a9a2c80 100644 --- a/core/java/android/gadget/GadgetManager.java +++ b/core/java/android/gadget/GadgetManager.java @@ -300,21 +300,5 @@ public class GadgetManager { throw new RuntimeException("system server dead?", e); } } - - /** - * Get the list of gadgetIds that have been bound to the given gadget - * provider. - * - * @param provider The {@link android.content.BroadcastReceiver} that is the - * gadget provider to find gadgetIds for. - */ - public int[] getGadgetIds(ComponentName provider) { - try { - return sService.getGadgetIds(provider); - } - catch (RemoteException e) { - throw new RuntimeException("system server dead?", e); - } - } } diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 106c920..40a5b47 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -48,6 +48,7 @@ public class Camera { private static final int ERROR_CALLBACK = 5; private int mNativeContext; // accessed by native methods + private int mListenerContext; private EventHandler mEventHandler; private ShutterCallback mShutterCallback; private PictureCallback mRawImageCallback; diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java index 0295f69..52f8209 100644 --- a/core/java/android/inputmethodservice/ExtractEditText.java +++ b/core/java/android/inputmethodservice/ExtractEditText.java @@ -98,13 +98,6 @@ public class ExtractEditText extends EditText { } /** - * Return true if the edit text is currently showing a scroll bar. - */ - public boolean hasVerticalScrollBar() { - return computeVerticalScrollRange() > computeVerticalScrollExtent(); - } - - /** * Pretend like the window this view is in always has focus, so its * highlight and cursor will be displayed. */ diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 1e2e2f3..4be1fc7 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -26,9 +26,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.IBinder; -import android.os.SystemClock; import android.provider.Settings; -import android.text.InputType; import android.text.Layout; import android.text.Spannable; import android.text.method.MovementMethod; @@ -51,7 +49,6 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.EditorInfo; -import android.widget.Button; import android.widget.FrameLayout; import java.io.FileDescriptor; @@ -244,8 +241,6 @@ public class InputMethodService extends AbstractInputMethodService { boolean mIsFullscreen; View mExtractView; ExtractEditText mExtractEditText; - ViewGroup mExtractAccessories; - Button mExtractAction; ExtractedText mExtractedText; int mExtractedToken; @@ -276,21 +271,6 @@ public class InputMethodService extends AbstractInputMethodService { } }; - final View.OnClickListener mActionClickListener = new View.OnClickListener() { - public void onClick(View v) { - final EditorInfo ei = getCurrentInputEditorInfo(); - final InputConnection ic = getCurrentInputConnection(); - if (ei != null && ic != null) { - if (ei.actionId != 0) { - ic.performEditorAction(ei.actionId); - } else if ((ei.imeOptions&EditorInfo.IME_MASK_ACTION) - != EditorInfo.IME_ACTION_NONE) { - ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION); - } - } - } - }; - /** * Concrete implementation of * {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides @@ -542,8 +522,6 @@ public class InputMethodService extends AbstractInputMethodService { mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea); mExtractView = null; mExtractEditText = null; - mExtractAccessories = null; - mExtractAction = null; mFullscreenApplied = false; mCandidatesFrame = (FrameLayout)mRootView.findViewById(android.R.id.candidatesArea); @@ -725,7 +703,7 @@ public class InputMethodService extends AbstractInputMethodService { setExtractView(v); } } - startExtractingText(false); + startExtractingText(); } } @@ -929,17 +907,9 @@ public class InputMethodService extends AbstractInputMethodService { mExtractEditText = (ExtractEditText)view.findViewById( com.android.internal.R.id.inputExtractEditText); mExtractEditText.setIME(this); - mExtractAction = (Button)view.findViewById( - com.android.internal.R.id.inputExtractAction); - if (mExtractAction != null) { - mExtractAccessories = (ViewGroup)view.findViewById( - com.android.internal.R.id.inputExtractAccessories); - } - startExtractingText(false); + startExtractingText(); } else { mExtractEditText = null; - mExtractAccessories = null; - mExtractAction = null; } } @@ -1196,7 +1166,7 @@ public class InputMethodService extends AbstractInputMethodService { } if (doShowInput) { - startExtractingText(false); + startExtractingText(); } if (!wasVisible) { @@ -1306,7 +1276,7 @@ public class InputMethodService extends AbstractInputMethodService { if (DEBUG) Log.v(TAG, "CALL: onStartInputView"); mInputViewStarted = true; onStartInputView(mInputEditorInfo, restarting); - startExtractingText(true); + startExtractingText(); } else if (mCandidatesVisibility == View.VISIBLE) { if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView"); mCandidatesViewStarted = true; @@ -1483,25 +1453,6 @@ public class InputMethodService extends AbstractInputMethodService { static final int MOVEMENT_DOWN = -1; static final int MOVEMENT_UP = -2; - void reportExtractedMovement(int keyCode, int count) { - int dx = 0, dy = 0; - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_LEFT: - dx = -count; - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - dx = count; - break; - case KeyEvent.KEYCODE_DPAD_UP: - dy = -count; - break; - case KeyEvent.KEYCODE_DPAD_DOWN: - dy = count; - break; - } - onExtractedCursorMovement(dx, dy); - } - boolean doMovementKey(int keyCode, KeyEvent event, int count) { final ExtractEditText eet = mExtractEditText; if (isFullscreenMode() && isInputViewShown() && eet != null) { @@ -1516,7 +1467,6 @@ public class InputMethodService extends AbstractInputMethodService { if (count == MOVEMENT_DOWN) { if (movement.onKeyDown(eet, (Spannable)eet.getText(), keyCode, event)) { - reportExtractedMovement(keyCode, 1); return true; } } else if (count == MOVEMENT_UP) { @@ -1525,9 +1475,7 @@ public class InputMethodService extends AbstractInputMethodService { return true; } } else { - if (movement.onKeyOther(eet, (Spannable)eet.getText(), event)) { - reportExtractedMovement(keyCode, count); - } else { + if (!movement.onKeyOther(eet, (Spannable)eet.getText(), event)) { KeyEvent down = new KeyEvent(event, KeyEvent.ACTION_DOWN); if (movement.onKeyDown(eet, (Spannable)eet.getText(), keyCode, down)) { @@ -1540,7 +1488,6 @@ public class InputMethodService extends AbstractInputMethodService { movement.onKeyUp(eet, (Spannable)eet.getText(), keyCode, up); } - reportExtractedMovement(keyCode, count); } } } @@ -1560,97 +1507,6 @@ public class InputMethodService extends AbstractInputMethodService { } /** - * Send the given key event code (as defined by {@link KeyEvent}) to the - * current input connection is a key down + key up event pair. The sent - * events have {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD} - * set, so that the recipient can identify them as coming from a software - * input method, and - * {@link KeyEvent#FLAG_KEEP_TOUCH_MODE KeyEvent.FLAG_KEEP_TOUCH_MODE}, so - * that they don't impact the current touch mode of the UI. - * - * @param keyEventCode The raw key code to send, as defined by - * {@link KeyEvent}. - */ - public void sendDownUpKeyEvents(int keyEventCode) { - InputConnection ic = getCurrentInputConnection(); - if (ic == null) return; - long eventTime = SystemClock.uptimeMillis(); - ic.sendKeyEvent(new KeyEvent(eventTime, eventTime, - KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, 0, 0, - KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)); - ic.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, - KeyEvent.ACTION_UP, keyEventCode, 0, 0, 0, 0, - KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)); - } - - /** - * Ask the input target to execute its default action via - * {@link InputConnection#performEditorAction - * InputConnection.performEditorAction()}. - * - * @param fromEnterKey If true, this will be executed as if the user had - * pressed an enter key on the keyboard, that is it will <em>not</em> - * be done if the editor has set {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION - * EditorInfo.IME_FLAG_NO_ENTER_ACTION}. If false, the action will be - * sent regardless of how the editor has set that flag. - * - * @return Returns a boolean indicating whether an action has been sent. - * If false, either the editor did not specify a default action or it - * does not want an action from the enter key. If true, the action was - * sent (or there was no input connection at all). - */ - public boolean sendDefaultEditorAction(boolean fromEnterKey) { - EditorInfo ei = getCurrentInputEditorInfo(); - if (ei != null && - (!fromEnterKey || (ei.imeOptions & - EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0) && - (ei.imeOptions & EditorInfo.IME_MASK_ACTION) != - EditorInfo.IME_ACTION_NONE) { - // If the enter key was pressed, and the editor has a default - // action associated with pressing enter, then send it that - // explicit action instead of the key event. - InputConnection ic = getCurrentInputConnection(); - if (ic != null) { - ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION); - } - return true; - } - - return false; - } - - /** - * Send the given UTF-16 character to the current input connection. Most - * characters will be delivered simply by calling - * {@link InputConnection#commitText InputConnection.commitText()} with - * the character; some, however, may be handled different. In particular, - * the enter character ('\n') will either be delivered as an action code - * or a raw key event, as appropriate. - * - * @param charCode The UTF-16 character code to send. - */ - public void sendKeyChar(char charCode) { - switch (charCode) { - case '\n': // Apps may be listening to an enter key to perform an action - if (!sendDefaultEditorAction(true)) { - sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER); - } - break; - default: - // Make sure that digits go through any text watcher on the client side. - if (charCode >= '0' && charCode <= '9') { - sendDownUpKeyEvents(charCode - '0' + KeyEvent.KEYCODE_0); - } else { - InputConnection ic = getCurrentInputConnection(); - if (ic != null) { - ic.commitText(String.valueOf((char) charCode), 1); - } - } - break; - } - } - - /** * 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 @@ -1666,36 +1522,11 @@ public class InputMethodService extends AbstractInputMethodService { /** * 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, but only if the extracted text - * editor has a vertical scroll bar because its text doesn't fit. - * Re-implement this to provide whatever behavior you want. + * the candidates view when this happens. Re-implement this to provide + * whatever behavior you want. */ public void onExtractedTextClicked() { - if (mExtractEditText == null) { - return; - } - if (mExtractEditText.hasVerticalScrollBar()) { - setCandidatesViewShown(false); - } - } - - /** - * This is called when the user has performed a cursor movement in the - * extracted text view, when it is running in fullscreen mode. The default - * implementation hides the candidates view when a vertical movement - * happens, but only if the extracted text editor has a vertical scroll bar - * because its text doesn't fit. - * Re-implement this to provide whatever behavior you want. - * @param dx The amount of cursor movement in the x dimension. - * @param dy The amount of cursor movement in the y dimension. - */ - public void onExtractedCursorMovement(int dx, int dy) { - if (mExtractEditText == null || dy == 0) { - return; - } - if (mExtractEditText.hasVerticalScrollBar()) { - setCandidatesViewShown(false); - } + setCandidatesViewShown(false); } /** @@ -1714,74 +1545,7 @@ public class InputMethodService extends AbstractInputMethodService { return true; } - /** - * Return text that can be used as a button label for the given - * {@link EditorInfo#imeOptions EditorInfo.imeOptions}. Returns null - * if there is no action requested. Note that there is no guarantee that - * the returned text will be relatively short, so you probably do not - * want to use it as text on a soft keyboard key label. - * - * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}. - * - * @return Returns a label to use, or null if there is no action. - */ - public CharSequence getTextForImeAction(int imeOptions) { - switch (imeOptions&EditorInfo.IME_MASK_ACTION) { - case EditorInfo.IME_ACTION_NONE: - return null; - case EditorInfo.IME_ACTION_GO: - return getText(com.android.internal.R.string.ime_action_go); - case EditorInfo.IME_ACTION_SEARCH: - return getText(com.android.internal.R.string.ime_action_search); - case EditorInfo.IME_ACTION_SEND: - return getText(com.android.internal.R.string.ime_action_send); - case EditorInfo.IME_ACTION_NEXT: - return getText(com.android.internal.R.string.ime_action_next); - default: - return getText(com.android.internal.R.string.ime_action_default); - } - } - - /** - * Called when it is time to update the actions available from a full-screen - * IME. You do not need to deal with this if you are using the standard - * full screen extract UI. If replacing it, you will need to re-implement - * this to put the action in your own UI and handle it. - */ - public void onUpdateExtractingAccessories(EditorInfo ei) { - if (mExtractAccessories == null) { - return; - } - final boolean hasAction = ei.actionLabel != null || ( - (ei.imeOptions&EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE && - (ei.imeOptions&EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0); - if (hasAction) { - mExtractAccessories.setVisibility(View.VISIBLE); - if (ei.actionLabel != null) { - mExtractAction.setText(ei.actionLabel); - } else { - mExtractAction.setText(getTextForImeAction(ei.imeOptions)); - } - mExtractAction.setOnClickListener(mActionClickListener); - } else { - mExtractAccessories.setVisibility(View.GONE); - mExtractAction.setOnClickListener(null); - } - } - - /** - * This is called when, while currently displayed in extract mode, the - * current input target changes. The default implementation will - * auto-hide the IME if the new target is not a full editor, since this - * can be an confusing experience for the user. - */ - public void onExtractingInputChanged(EditorInfo ei) { - if (ei.inputType == InputType.TYPE_NULL) { - dismissSoftInput(InputMethodManager.HIDE_NOT_ALWAYS); - } - } - - void startExtractingText(boolean inputChanged) { + void startExtractingText() { final ExtractEditText eet = mExtractEditText; if (eet != null && getCurrentInputStarted() && isFullscreenMode()) { @@ -1793,13 +1557,9 @@ public class InputMethodService extends AbstractInputMethodService { req.hintMaxChars = 10000; mExtractedText = getCurrentInputConnection().getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR); - - final EditorInfo ei = getCurrentInputEditorInfo(); - try { eet.startInternalChanges(); - onUpdateExtractingAccessories(ei); - int inputType = ei.inputType; + int inputType = getCurrentInputEditorInfo().inputType; if ((inputType&EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) { if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) { @@ -1807,7 +1567,7 @@ public class InputMethodService extends AbstractInputMethodService { } } eet.setInputType(inputType); - eet.setHint(ei.hintText); + eet.setHint(mInputEditorInfo.hintText); if (mExtractedText != null) { eet.setEnabled(true); eet.setExtractedText(mExtractedText); @@ -1818,10 +1578,6 @@ public class InputMethodService extends AbstractInputMethodService { } finally { eet.finishInternalChanges(); } - - if (inputChanged) { - onExtractingInputChanged(ei); - } } } diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java index c838779..886e688 100755 --- a/core/java/android/inputmethodservice/KeyboardView.java +++ b/core/java/android/inputmethodservice/KeyboardView.java @@ -74,6 +74,7 @@ public class KeyboardView extends View implements View.OnClickListener { * For keys that repeat, this is only called once. * @param primaryCode the unicode of the key being pressed. If the touch is not on a valid * key, the value will be zero. + * @hide Pending API Council approval */ void onPress(int primaryCode); @@ -81,6 +82,7 @@ public class KeyboardView extends View implements View.OnClickListener { * Called when the user releases a key. This is sent after the {@link #onKey} is called. * For keys that repeat, this is only called once. * @param primaryCode the code of the key that was released + * @hide Pending API Council approval */ void onRelease(int primaryCode); @@ -97,12 +99,6 @@ public class KeyboardView extends View implements View.OnClickListener { void onKey(int primaryCode, int[] keyCodes); /** - * Sends a sequence of characters to the listener. - * @param text the sequence of characters to be displayed. - */ - void onText(CharSequence text); - - /** * Called when the user quickly moves the finger from right to left. */ void swipeLeft(); @@ -398,7 +394,6 @@ public class KeyboardView extends View implements View.OnClickListener { requestLayout(); invalidate(); computeProximityThreshold(keyboard); - mMiniKeyboardCache.clear(); // Not really necessary to do every time, but will free up views } /** @@ -704,7 +699,9 @@ public class KeyboardView extends View implements View.OnClickListener { if (index != NOT_A_KEY && index < mKeys.length) { final Key key = mKeys[index]; if (key.text != null) { - mKeyboardActionListener.onText(key.text); + for (int i = 0; i < key.text.length(); i++) { + mKeyboardActionListener.onKey(key.text.charAt(i), key.codes); + } mKeyboardActionListener.onRelease(NOT_A_KEY); } else { int code = key.codes[0]; @@ -795,7 +792,7 @@ public class KeyboardView extends View implements View.OnClickListener { mPreviewText.setCompoundDrawables(null, null, null, null); mPreviewText.setText(getPreviewText(key)); if (key.label.length() > 1 && key.codes.length < 2) { - mPreviewText.setTextSize(mKeyTextSize); + mPreviewText.setTextSize(mLabelTextSize); mPreviewText.setTypeface(Typeface.DEFAULT_BOLD); } else { mPreviewText.setTextSize(mPreviewTextSizeLarge); @@ -899,11 +896,6 @@ public class KeyboardView extends View implements View.OnClickListener { dismissPopupKeyboard(); } - public void onText(CharSequence text) { - mKeyboardActionListener.onText(text); - dismissPopupKeyboard(); - } - public void swipeLeft() { } public void swipeRight() { } public void swipeUp() { } @@ -1110,8 +1102,6 @@ public class KeyboardView extends View implements View.OnClickListener { mHandler.removeMessages(MSG_SHOW_PREVIEW); dismissPopupKeyboard(); - - mMiniKeyboardCache.clear(); } @Override diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java index ccef97e..f816caa 100644 --- a/core/java/android/net/SSLCertificateSocketFactory.java +++ b/core/java/android/net/SSLCertificateSocketFactory.java @@ -16,46 +16,39 @@ package android.net; -import android.content.Context; +import android.util.Log; +import android.util.Config; import android.net.http.DomainNameChecker; import android.os.SystemProperties; -import android.util.Config; -import android.util.Log; -import com.android.internal.net.SSLSessionCache; +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; -import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; +import java.security.GeneralSecurityException; import java.security.cert.Certificate; import java.security.cert.X509Certificate; -import javax.net.SocketFactory; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; - -/** - * SSLSocketFactory that allows skipping the certificate chain validation - * based on system setting (socket.relaxsslcheck=yes, ro.secure=1 - for - * testing only). - * - * It also adds a readTimeout that will be set on each created socket. - * The factory will use SSL session persistence if enabled by config. - */ public class SSLCertificateSocketFactory extends SSLSocketFactory { + private static final boolean DBG = true; private static final String LOG_TAG = "SSLCertificateSocketFactory"; private static X509TrustManager sDefaultTrustManager; + private final int socketReadTimeoutForSslHandshake; + static { try { TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509"); @@ -90,36 +83,14 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } }; - private static SSLSocketFactory factory; - - /** - * Initialize a single default factory to be used for all returned - * sockets. - * - * Because of the signature of getDefault(int timeout) it needs to create - * a new instance which encapsulates the timeout on each call. We want - * to share a single SSLContext and SSLSessionCache. - * - * Can be called multiple times - but only the first will initialize the factory. - * - * @param androidContext will be used for SSL session persistence. Null for backward - * compatibility, no SSL persistence. - * @hide - */ - public static synchronized void setupDefaultFactory(Context androidContext) { - if ( factory != null) { - // Can only be initialized once, to avoid having multiple caches. - return; - } - factory = SSLSessionCache.getSocketFactory(androidContext, TRUST_MANAGER); - } - - private final int socketReadTimeoutForSslHandshake; + private SSLSocketFactory factory; public SSLCertificateSocketFactory(int socketReadTimeoutForSslHandshake) throws NoSuchAlgorithmException, KeyManagementException { - this.socketReadTimeoutForSslHandshake - = socketReadTimeoutForSslHandshake; + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, TRUST_MANAGER, new java.security.SecureRandom()); + factory = (SSLSocketFactory) context.getSocketFactory(); + this.socketReadTimeoutForSslHandshake = socketReadTimeoutForSslHandshake; } /** @@ -132,11 +103,6 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { */ public static SocketFactory getDefault(int socketReadTimeoutForSslHandshake) { try { - if (factory == null) { - // The delegated factory was not initialized explicitely with a context. - // Use a default one. - setupDefaultFactory(null); - } return new SSLCertificateSocketFactory(socketReadTimeoutForSslHandshake); } catch (NoSuchAlgorithmException e) { Log.e(LOG_TAG, diff --git a/core/java/android/net/http/AndroidHttpClient.java b/core/java/android/net/http/AndroidHttpClient.java index 0c4fcda..4fb1499 100644 --- a/core/java/android/net/http/AndroidHttpClient.java +++ b/core/java/android/net/http/AndroidHttpClient.java @@ -47,8 +47,6 @@ import org.apache.http.params.HttpProtocolParams; import org.apache.http.protocol.BasicHttpProcessor; import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.BasicHttpContext; -import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache; -import org.apache.harmony.xnet.provider.jsse.SSLContextImpl; import java.io.IOException; import java.io.InputStream; @@ -57,7 +55,6 @@ import java.io.OutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import java.net.URI; -import java.security.KeyManagementException; import android.util.Log; import android.content.ContentResolver; @@ -101,13 +98,10 @@ public final class AndroidHttpClient implements HttpClient { /** * Create a new HttpClient with reasonable defaults (which you can update). - * * @param userAgent to report in your HTTP requests. - * @param sessionCache persistent session cache * @return AndroidHttpClient for you to use for all your requests. */ - public static AndroidHttpClient newInstance(String userAgent, - SSLClientSessionCache sessionCache) { + public static AndroidHttpClient newInstance(String userAgent) { HttpParams params = new BasicHttpParams(); // Turn off stale checking. Our connections break all the time anyway, @@ -129,8 +123,7 @@ public final class AndroidHttpClient implements HttpClient { schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); schemeRegistry.register(new Scheme("https", - socketFactoryWithCache(sessionCache), 443)); - + SSLSocketFactory.getSocketFactory(), 443)); ClientConnectionManager manager = new ThreadSafeClientConnManager(params, schemeRegistry); @@ -139,41 +132,6 @@ public final class AndroidHttpClient implements HttpClient { return new AndroidHttpClient(manager, params); } - /** - * Returns a socket factory backed by the given persistent session cache. - * - * @param sessionCache to retrieve sessions from, null for no cache - */ - private static SSLSocketFactory socketFactoryWithCache( - SSLClientSessionCache sessionCache) { - if (sessionCache == null) { - // Use the default factory which doesn't support persistent - // caching. - return SSLSocketFactory.getSocketFactory(); - } - - // Create a new SSL context backed by the cache. - // TODO: Keep a weak *identity* hash map of caches to engines. In the - // mean time, if we have two engines for the same cache, they'll still - // share sessions but will have to do so through the persistent cache. - SSLContextImpl sslContext = new SSLContextImpl(); - try { - sslContext.engineInit(null, null, null, sessionCache, null); - } catch (KeyManagementException e) { - throw new AssertionError(e); - } - return new SSLSocketFactory(sslContext.engineGetSocketFactory()); - } - - /** - * Create a new HttpClient with reasonable defaults (which you can update). - * @param userAgent to report in your HTTP requests. - * @return AndroidHttpClient for you to use for all your requests. - */ - public static AndroidHttpClient newInstance(String userAgent) { - return newInstance(userAgent, null /* session cache */); - } - private final HttpClient delegate; private RuntimeException mLeakedException = new IllegalStateException( diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java index 0edbe5b..b7f7368 100644 --- a/core/java/android/net/http/CertificateChainValidator.java +++ b/core/java/android/net/http/CertificateChainValidator.java @@ -16,6 +16,8 @@ package android.net.http; +import android.os.SystemClock; + import java.io.IOException; import java.security.cert.Certificate; @@ -26,13 +28,23 @@ import java.security.cert.X509Certificate; import java.security.GeneralSecurityException; import java.security.KeyStore; +import java.util.Arrays; +import java.util.Date; +import java.util.Enumeration; + +import javax.net.ssl.SSLContext; import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; +import org.apache.http.HttpHost; + +import org.bouncycastle.asn1.x509.X509Name; + /** * Class responsible for all server certificate validation functionality * @@ -40,6 +52,9 @@ import javax.net.ssl.X509TrustManager; */ class CertificateChainValidator { + private static long sTotal = 0; + private static long sTotalReused = 0; + /** * The singleton instance of the certificate chain validator */ @@ -95,42 +110,91 @@ class CertificateChainValidator { * @return An SSL error object if there is an error and null otherwise */ public SslError doHandshakeAndValidateServerCertificates( - HttpsConnection connection, SSLSocket sslSocket, String domain) - throws IOException { - X509Certificate[] serverCertificates = null; + HttpsConnection connection, SSLSocket sslSocket, String domain) + throws SSLHandshakeException, IOException { - // start handshake, close the socket if we fail - try { - sslSocket.setUseClientMode(true); - sslSocket.startHandshake(); - } catch (IOException e) { - closeSocketThrowException( - sslSocket, e.getMessage(), - "failed to perform SSL handshake"); + ++sTotal; + + SSLContext sslContext = HttpsConnection.getContext(); + if (sslContext == null) { + closeSocketThrowException(sslSocket, "SSL context is null"); } - // retrieve the chain of the server peer certificates - Certificate[] peerCertificates = - sslSocket.getSession().getPeerCertificates(); + X509Certificate[] serverCertificates = null; - if (peerCertificates == null || peerCertificates.length <= 0) { - closeSocketThrowException( - sslSocket, "failed to retrieve peer certificates"); - } else { - serverCertificates = - new X509Certificate[peerCertificates.length]; - for (int i = 0; i < peerCertificates.length; ++i) { - serverCertificates[i] = - (X509Certificate)(peerCertificates[i]); + long sessionBeforeHandshakeLastAccessedTime = 0; + byte[] sessionBeforeHandshakeId = null; + + SSLSession sessionAfterHandshake = null; + + synchronized(sslContext) { + // get SSL session before the handshake + SSLSession sessionBeforeHandshake = + getSSLSession(sslContext, connection.getHost()); + if (sessionBeforeHandshake != null) { + sessionBeforeHandshakeLastAccessedTime = + sessionBeforeHandshake.getLastAccessedTime(); + + sessionBeforeHandshakeId = + sessionBeforeHandshake.getId(); + } + + // start handshake, close the socket if we fail + try { + sslSocket.setUseClientMode(true); + sslSocket.startHandshake(); + } catch (IOException e) { + closeSocketThrowException( + sslSocket, e.getMessage(), + "failed to perform SSL handshake"); } - // update the SSL certificate associated with the connection - if (connection != null) { - if (serverCertificates[0] != null) { - connection.setCertificate( - new SslCertificate(serverCertificates[0])); + // retrieve the chain of the server peer certificates + Certificate[] peerCertificates = + sslSocket.getSession().getPeerCertificates(); + + if (peerCertificates == null || peerCertificates.length <= 0) { + closeSocketThrowException( + sslSocket, "failed to retrieve peer certificates"); + } else { + serverCertificates = + new X509Certificate[peerCertificates.length]; + for (int i = 0; i < peerCertificates.length; ++i) { + serverCertificates[i] = + (X509Certificate)(peerCertificates[i]); + } + + // update the SSL certificate associated with the connection + if (connection != null) { + if (serverCertificates[0] != null) { + connection.setCertificate( + new SslCertificate(serverCertificates[0])); + } } } + + // get SSL session after the handshake + sessionAfterHandshake = + getSSLSession(sslContext, connection.getHost()); + } + + if (sessionBeforeHandshakeLastAccessedTime != 0 && + sessionAfterHandshake != null && + Arrays.equals( + sessionBeforeHandshakeId, sessionAfterHandshake.getId()) && + sessionBeforeHandshakeLastAccessedTime < + sessionAfterHandshake.getLastAccessedTime()) { + + if (HttpLog.LOGV) { + HttpLog.v("SSL session was reused: total reused: " + + sTotalReused + + " out of total of: " + sTotal); + + ++sTotalReused; + } + + // no errors!!! + return null; } // check if the first certificate in the chain is for this site @@ -152,6 +216,7 @@ class CertificateChainValidator { } } + // // first, we validate the chain using the standard validation // solution; if we do not find any errors, we are done; if we // fail the standard validation, we re-validate again below, @@ -328,14 +393,14 @@ class CertificateChainValidator { } private void closeSocketThrowException( - SSLSocket socket, String errorMessage, String defaultErrorMessage) - throws IOException { + SSLSocket socket, String errorMessage, String defaultErrorMessage) + throws SSLHandshakeException, IOException { closeSocketThrowException( socket, errorMessage != null ? errorMessage : defaultErrorMessage); } - private void closeSocketThrowException(SSLSocket socket, - String errorMessage) throws IOException { + private void closeSocketThrowException(SSLSocket socket, String errorMessage) + throws SSLHandshakeException, IOException { if (HttpLog.LOGV) { HttpLog.v("validation error: " + errorMessage); } @@ -351,4 +416,29 @@ class CertificateChainValidator { throw new SSLHandshakeException(errorMessage); } + + /** + * @param sslContext The SSL context shared accross all the SSL sessions + * @param host The host associated with the session + * @return A suitable SSL session from the SSL context + */ + private SSLSession getSSLSession(SSLContext sslContext, HttpHost host) { + if (sslContext != null && host != null) { + Enumeration en = sslContext.getClientSessionContext().getIds(); + while (en.hasMoreElements()) { + byte[] id = (byte[]) en.nextElement(); + if (id != null) { + SSLSession session = + sslContext.getClientSessionContext().getSession(id); + if (session.isValid() && + host.getHostName().equals(session.getPeerHost()) && + host.getPort() == session.getPeerPort()) { + return session; + } + } + } + } + + return null; + } } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 7590bfe..6f9d6c6 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -16,8 +16,6 @@ import android.util.SparseArray; */ public abstract class BatteryStats implements Parcelable { - private static final boolean LOCAL_LOGV = false; - /** * A constant indicating a partial wake lock timer. */ @@ -74,7 +72,6 @@ public abstract class BatteryStats implements Parcelable { private static final String WAKELOCK_DATA = "wakelock"; private static final String NETWORK_DATA = "network"; private static final String BATTERY_DATA = "battery"; - private static final String MISC_DATA = "misc"; private final StringBuilder mFormatBuilder = new StringBuilder(8); private final Formatter mFormatter = new Formatter(mFormatBuilder); @@ -96,11 +93,11 @@ public abstract class BatteryStats implements Parcelable { * Returns the total time in microseconds associated with this Timer for the * selected type of statistics. * - * @param batteryRealtime system realtime on battery in microseconds + * @param now system uptime time in microseconds * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT * @return a time in microseconds */ - public abstract long getTotalTime(long batteryRealtime, int which); + public abstract long getTotalTime(long now, int which); /** * Temporary for debugging. @@ -225,11 +222,11 @@ public abstract class BatteryStats implements Parcelable { /** * Returns the amount of time spent started. * - * @param batteryUptime elapsed uptime on battery in microseconds. + * @param now elapsed realtime in microseconds. * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT. * @return */ - public abstract long getStartTime(long batteryUptime, int which); + public abstract long getStartTime(long now, int which); /** * Returns the total number of times startService() has been called. @@ -259,16 +256,16 @@ public abstract class BatteryStats implements Parcelable { * * {@hide} */ - public abstract long getScreenOnTime(long batteryRealtime, int which); + public abstract long getBatteryScreenOnTime(); /** - * Returns the time in milliseconds that the phone has been on while the device was - * running on battery. + * Returns the time in milliseconds that the screen has been on while the device was + * plugged in. * * {@hide} */ - public abstract long getPhoneOnTime(long batteryRealtime, int which); - + public abstract long getPluggedScreenOnTime(); + /** * Return whether we are currently running on battery. */ @@ -385,18 +382,18 @@ public abstract class BatteryStats implements Parcelable { * * @param sb a StringBuilder object. * @param timer a Timer object contining the wakelock times. - * @param batteryRealtime the current on-battery time in microseconds. + * @param now the current time in microseconds. * @param name the name of the wakelock. * @param which which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT. * @param linePrefix a String to be prepended to each line of output. * @return the line prefix */ - private static final String printWakeLock(StringBuilder sb, Timer timer, - long batteryRealtime, String name, int which, String linePrefix) { + private static final String printWakeLock(StringBuilder sb, Timer timer, long now, + String name, int which, String linePrefix) { if (timer != null) { // Convert from microseconds to milliseconds with rounding - long totalTimeMicros = timer.getTotalTime(batteryRealtime, which); + long totalTimeMicros = timer.getTotalTime(now, which); long totalTimeMillis = (totalTimeMicros + 500) / 1000; int count = timer.getCount(which); @@ -473,30 +470,26 @@ public abstract class BatteryStats implements Parcelable { * @param which */ private final void dumpCheckinLocked(PrintWriter pw, int which) { - final long rawUptime = SystemClock.uptimeMillis() * 1000; - final long rawRealtime = SystemClock.elapsedRealtime() * 1000; - final long batteryUptime = getBatteryUptime(rawUptime); - final long batteryRealtime = getBatteryRealtime(rawRealtime); - final long whichBatteryUptime = computeBatteryUptime(rawUptime, which); - final long whichBatteryRealtime = computeBatteryRealtime(rawRealtime, which); - final long totalRealtime = computeRealtime(rawRealtime, which); - final long totalUptime = computeUptime(rawUptime, which); - final long screenOnTime = getScreenOnTime(batteryRealtime, which); - final long phoneOnTime = getPhoneOnTime(batteryRealtime, which); + long uSecTime = SystemClock.elapsedRealtime() * 1000; + final long uSecNow = getBatteryUptime(uSecTime); StringBuilder sb = new StringBuilder(128); + long batteryUptime = computeBatteryUptime(uSecNow, which); + long batteryRealtime = computeBatteryRealtime(getBatteryRealtime(uSecTime), which); + long elapsedRealtime = computeRealtime(uSecTime, which); + long uptime = computeUptime(SystemClock.uptimeMillis() * 1000, which); String category = STAT_NAMES[which]; // Dump "battery" stat dumpLine(pw, 0 /* uid */, category, BATTERY_DATA, which == STATS_TOTAL ? getStartCount() : "N/A", - whichBatteryUptime / 1000, whichBatteryRealtime / 1000, - totalUptime / 1000, totalRealtime / 1000); - - // Dump misc stats - dumpLine(pw, 0 /* uid */, category, MISC_DATA, - screenOnTime / 1000, phoneOnTime / 1000); + batteryUptime / 1000, + formatRatioLocked(batteryUptime, elapsedRealtime), + batteryRealtime / 1000, + formatRatioLocked(batteryRealtime, elapsedRealtime), + uptime / 1000, + elapsedRealtime / 1000); SparseArray<? extends Uid> uidStats = getUidStats(); final int NU = uidStats.size(); @@ -515,11 +508,11 @@ public abstract class BatteryStats implements Parcelable { Uid.Wakelock wl = ent.getValue(); String linePrefix = ""; sb.setLength(0); - linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_FULL), batteryRealtime, + linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_FULL), uSecNow, "full", which, linePrefix); - linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_PARTIAL), batteryRealtime, + linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_PARTIAL), uSecNow, "partial", which, linePrefix); - linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_WINDOW), batteryRealtime, + linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_WINDOW), uSecNow, "window", which, linePrefix); // Only log if we had at lease one wakelock... @@ -538,7 +531,7 @@ public abstract class BatteryStats implements Parcelable { Timer timer = se.getSensorTime(); if (timer != null) { // Convert from microseconds to milliseconds with rounding - long totalTime = (timer.getTotalTime(batteryRealtime, which) + 500) / 1000; + long totalTime = (timer.getTotalTime(uSecNow, which) + 500) / 1000; int count = timer.getCount(which); if (totalTime != 0) { dumpLine(pw, uid, category, SENSOR_DATA, sensorNumber, totalTime, count); @@ -578,7 +571,7 @@ public abstract class BatteryStats implements Parcelable { for (Map.Entry<String, ? extends BatteryStats.Uid.Pkg.Serv> sent : serviceStats.entrySet()) { BatteryStats.Uid.Pkg.Serv ss = sent.getValue(); - long startTime = ss.getStartTime(batteryUptime, which); + long startTime = ss.getStartTime(uSecNow, which); int starts = ss.getStarts(which); int launches = ss.getLaunches(which); if (startTime != 0 || starts != 0 || launches != 0) { @@ -598,40 +591,29 @@ public abstract class BatteryStats implements Parcelable { @SuppressWarnings("unused") private final void dumpLocked(Printer pw, String prefix, int which) { - final long rawUptime = SystemClock.uptimeMillis() * 1000; - final long rawRealtime = SystemClock.elapsedRealtime() * 1000; - final long batteryUptime = getBatteryUptime(rawUptime); - final long batteryRealtime = getBatteryUptime(rawRealtime); - - final long whichBatteryUptime = computeBatteryUptime(rawUptime, which); - final long whichBatteryRealtime = computeBatteryRealtime(rawRealtime, which); - final long totalRealtime = computeRealtime(rawRealtime, which); - final long totalUptime = computeUptime(rawUptime, which); - + long uSecTime = SystemClock.elapsedRealtime() * 1000; + final long uSecNow = getBatteryUptime(uSecTime); + StringBuilder sb = new StringBuilder(128); + long batteryUptime = computeBatteryUptime(uSecNow, which); + long batteryRealtime = computeBatteryRealtime(getBatteryRealtime(uSecTime), which); + long elapsedRealtime = computeRealtime(uSecTime, which); + long uptime = computeUptime(SystemClock.uptimeMillis() * 1000, which); pw.println(prefix - + " Time on battery: " + formatTimeMs(whichBatteryUptime / 1000) - + "(" + formatRatioLocked(whichBatteryUptime, totalRealtime) + + " Time on battery: " + formatTimeMs(batteryUptime / 1000) + "(" + + formatRatioLocked(batteryUptime, elapsedRealtime) + ") uptime, " - + formatTimeMs(whichBatteryRealtime / 1000) + "(" - + formatRatioLocked(whichBatteryRealtime, totalRealtime) + + formatTimeMs(batteryRealtime / 1000) + "(" + + formatRatioLocked(batteryRealtime, elapsedRealtime) + ") realtime"); pw.println(prefix + " Total: " - + formatTimeMs(totalUptime / 1000) + + formatTimeMs(uptime / 1000) + "uptime, " - + formatTimeMs(totalRealtime / 1000) + + formatTimeMs(elapsedRealtime / 1000) + "realtime"); - long screenOnTime = getScreenOnTime(batteryRealtime, which); - long phoneOnTime = getPhoneOnTime(batteryRealtime, which); - pw.println(prefix - + " Time with screen on: " + formatTimeMs(screenOnTime / 1000) - + "(" + formatRatioLocked(screenOnTime, whichBatteryRealtime) - + "), time with phone on: " + formatTimeMs(phoneOnTime / 1000) - + "(" + formatRatioLocked(phoneOnTime, whichBatteryRealtime) + ")"); - pw.println(" "); SparseArray<? extends Uid> uidStats = getUidStats(); @@ -659,11 +641,11 @@ public abstract class BatteryStats implements Parcelable { sb.append(prefix); sb.append(" Wake lock "); sb.append(ent.getKey()); - linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_FULL), batteryRealtime, + linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_FULL), uSecNow, "full", which, linePrefix); - linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_PARTIAL), batteryRealtime, + linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_PARTIAL), uSecNow, "partial", which, linePrefix); - linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_WINDOW), batteryRealtime, + linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_WINDOW), uSecNow, "window", which, linePrefix); if (!linePrefix.equals(": ")) { sb.append(" realtime"); @@ -695,7 +677,7 @@ public abstract class BatteryStats implements Parcelable { Timer timer = se.getSensorTime(); if (timer != null) { // Convert from microseconds to milliseconds with rounding - long totalTime = (timer.getTotalTime(batteryRealtime, which) + 500) / 1000; + long totalTime = (timer.getTotalTime(uSecNow, which) + 500) / 1000; int count = timer.getCount(which); //timer.logState(); if (totalTime != 0) { @@ -755,7 +737,7 @@ public abstract class BatteryStats implements Parcelable { for (Map.Entry<String, ? extends BatteryStats.Uid.Pkg.Serv> sent : serviceStats.entrySet()) { BatteryStats.Uid.Pkg.Serv ss = sent.getValue(); - long startTime = ss.getStartTime(batteryUptime, which); + long startTime = ss.getStartTime(uSecNow, which); int starts = ss.getStarts(which); int launches = ss.getLaunches(which); if (startTime != 0 || starts != 0 || launches != 0) { @@ -805,24 +787,9 @@ public abstract class BatteryStats implements Parcelable { @SuppressWarnings("unused") public void dumpCheckinLocked(PrintWriter pw, String[] args) { - boolean isUnpluggedOnly = false; - - for (String arg : args) { - if ("-u".equals(arg)) { - if (LOCAL_LOGV) Log.v("BatteryStats", "Dumping unplugged data"); - isUnpluggedOnly = true; - } - } - - if (isUnpluggedOnly) { - dumpCheckinLocked(pw, STATS_UNPLUGGED); - } - else { - dumpCheckinLocked(pw, STATS_TOTAL); - dumpCheckinLocked(pw, STATS_LAST); - dumpCheckinLocked(pw, STATS_UNPLUGGED); - dumpCheckinLocked(pw, STATS_CURRENT); - } + dumpCheckinLocked(pw, STATS_TOTAL); + dumpCheckinLocked(pw, STATS_LAST); + dumpCheckinLocked(pw, STATS_UNPLUGGED); + dumpCheckinLocked(pw, STATS_CURRENT); } - } diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 3fcb18e..ed138cb 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -76,11 +76,6 @@ public class ParcelFileDescriptor implements Parcelable { public static final int MODE_TRUNCATE = 0x04000000; /** - * For use with {@link #open}: append to end of file while writing. - */ - public static final int MODE_APPEND = 0x02000000; - - /** * Create a new ParcelFileDescriptor accessing a given file. * * @param file The file to be opened. @@ -143,19 +138,6 @@ public class ParcelFileDescriptor implements Parcelable { } /** - * Return the total size of the file representing this fd, as determined - * by stat(). Returns -1 if the fd is not a file. - */ - public native long getStatSize(); - - /** - * This is needed for implementing AssetFileDescriptor.AutoCloseOutputStream, - * and I really don't think we want it to be public. - * @hide - */ - public native long seekTo(long pos); - - /** * Close the ParcelFileDescriptor. This implementation closes the underlying * OS resources allocated to represent this stream. * diff --git a/core/java/android/pim/ICalendar.java b/core/java/android/pim/ICalendar.java index cc0f45e..4a5d7e4 100644 --- a/core/java/android/pim/ICalendar.java +++ b/core/java/android/pim/ICalendar.java @@ -405,15 +405,13 @@ public class ICalendar { // TODO: get rid of this -- handle all of the parsing in one pass through // the text. private static String normalizeText(String text) { + // first we deal with line folding, by replacing all "\r\n " strings + // with nothing + text = text.replaceAll("\r\n ", ""); + // it's supposed to be \r\n, but not everyone does that text = text.replaceAll("\r\n", "\n"); text = text.replaceAll("\r", "\n"); - - // we deal with line folding, by replacing all "\n " strings - // with nothing. The RFC specifies "\r\n " to be folded, but - // we handle "\n " and "\r " too because we can get those. - text = text.replaceAll("\n ", ""); - return text; } @@ -442,7 +440,7 @@ public class ICalendar { current = parseLine(line, state, current); // if the provided component was null, we will return the root // NOTE: in this case, if the first line is not a BEGIN, a - // FormatException will get thrown. + // FormatException will get thrown. if (component == null) { component = current; } @@ -526,7 +524,8 @@ public class ICalendar { private static String extractValue(ParserState state) throws FormatException { String line = state.line; - if (state.index >= line.length() || line.charAt(state.index) != ':') { + char c = line.charAt(state.index); + if (c != ':') { throw new FormatException("Expected ':' before end of line in " + line); } diff --git a/core/java/android/provider/Checkin.java b/core/java/android/provider/Checkin.java index 1454089..0cdac53 100644 --- a/core/java/android/provider/Checkin.java +++ b/core/java/android/provider/Checkin.java @@ -62,9 +62,6 @@ public final class Checkin { AUTOTEST_FAILURE, AUTOTEST_SEQUENCE_BEGIN, AUTOTEST_SUITE_BEGIN, - AUTOTEST_TCPDUMP_BEGIN, - AUTOTEST_TCPDUMP_DATA, - AUTOTEST_TCPDUMP_END, AUTOTEST_TEST_BEGIN, AUTOTEST_TEST_FAILURE, AUTOTEST_TEST_SUCCESS, @@ -101,14 +98,14 @@ public final class Checkin { SETUP_SERVER_ERROR, SETUP_SERVER_TIMEOUT, SETUP_NO_DATA_NETWORK, + SYSTEM_APP_NOT_RESPONDING, SYSTEM_BOOT, SYSTEM_LAST_KMSG, SYSTEM_RECOVERY_LOG, SYSTEM_RESTART, SYSTEM_SERVICE_LOOPING, SYSTEM_TOMBSTONE, - TEST, - BATTERY_DISCHARGE_INFO, + TEST, } } @@ -193,9 +190,6 @@ public final class Checkin { // The category is used for GTalk service messages public static final String CATEGORY = "android.server.checkin.CHECKIN"; - - // If true indicates that the checkin should only transfer market related data - public static final String EXTRA_MARKET_ONLY = "market_only"; } private static final String TAG = "Checkin"; diff --git a/core/java/android/provider/Im.java b/core/java/android/provider/Im.java index 19ad158..bea857f 100644 --- a/core/java/android/provider/Im.java +++ b/core/java/android/provider/Im.java @@ -2044,37 +2044,4 @@ public class Im { */ public static final Uri CONTENT_URI = Uri.parse("content://im/lastRmqId"); } - - /** - * Columns for IM branding resource map cache table. This table caches the result of - * loading the branding resources to speed up IM landing page start. - */ - public interface BrandingResourceMapCacheColumns { - /** - * The provider ID - * <P>Type: INTEGER</P> - */ - String PROVIDER_ID = "provider_id"; - /** - * The application resource ID - * <P>Type: INTEGER</P> - */ - String APP_RES_ID = "app_res_id"; - /** - * The plugin resource ID - * <P>Type: INTEGER</P> - */ - String PLUGIN_RES_ID = "plugin_res_id"; - } - - /** - * The table for caching the result of loading IM branding resources. - */ - public static final class BrandingResourceMapCache - implements BaseColumns, BrandingResourceMapCacheColumns { - /** - * The content:// style URL for this table. - */ - public static final Uri CONTENT_URI = Uri.parse("content://im/brandingResMapCache"); - } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 10ca5d5..4a784c8 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2183,12 +2183,6 @@ public final class Settings { public static final String CHECKIN_INTERVAL = "checkin_interval"; /** - * Boolean indicating if the market app should force market only checkins on - * install/uninstall. Any non-0 value is considered true. - */ - public static final String MARKET_FORCE_CHECKIN = "market_force_checkin"; - - /** * How frequently (in seconds) to check the memory status of the * device. */ @@ -2461,14 +2455,6 @@ public final class Settings { "gtalk_ssl_handshake_timeout_ms"; /** - * Enable use of ssl session caching. - * 'db' - save each session in a per-process database - * not set or any other value - normal java in-memory caching. - * Other cache types may be added. - */ - public static final String SSL_SESSION_CACHE = "ssl_session_cache"; - - /** * How many bytes long a message has to be, in order to be gzipped. */ public static final String SYNC_MIN_GZIP_BYTES = @@ -2780,29 +2766,11 @@ public final class Settings { public static final String VOICE_SEARCH_ENCODING_WIFI = "voice_search_encoding_wifi"; /** - * Whether to use automatic gain control in voice search (0 = disable, 1 = enable). - * To be factored out of this class. + * Prefix for rules that launch automatic instrumentation test cycles. + * The format is the InstrumentationTestRunner (or compatible) package and class, + * followed optionally by space-separated key value pairs to pass to it. */ - public static final String VOICE_SEARCH_ENABLE_AGC = "voice_search_enable_agc"; - - /** - * Whether to use noise suppression in voice search (0 = disable, 1 = enable). - * To be factored out of this class. - */ - public static final String VOICE_SEARCH_ENABLE_NS = "voice_search_enable_ns"; - - /** - * Whether to use the IIR filter in voice search (0 = disable, 1 = enable). - * To be factored out of this class. - */ - public static final String VOICE_SEARCH_ENABLE_IIR = "voice_search_enable_iir"; - - /** - * List of test suites (local disk filename) for the automatic instrumentation test runner. - * The file format is similar to automated_suites.xml, see AutoTesterService. - * If this setting is missing or empty, the automatic test runner will not start. - */ - public static final String AUTOTEST_SUITES_FILE = "autotest_suites_file"; + public static final String AUTOTEST_PREFIX = "autotest:"; /** * Interval between synchronous checkins forced by the automatic test runner. @@ -2817,15 +2785,6 @@ public final class Settings { */ public static final String AUTOTEST_REBOOT_SECONDS = "autotest_reboot_seconds"; - - /** - * Threshold values for the duration and level of a discharge cycle, under - * which we log discharge cycle info. - */ - public static final String BATTERY_DISCHARGE_DURATION_THRESHOLD = - "battery_discharge_duration_threshold"; - public static final String BATTERY_DISCHARGE_THRESHOLD = "battery_discharge_threshold"; - /** * @deprecated * @hide diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java index f8bc765..58f9491 100644 --- a/core/java/android/server/BluetoothA2dpService.java +++ b/core/java/android/server/BluetoothA2dpService.java @@ -15,7 +15,8 @@ */ /** - * TODO: Move this to services.jar + * TODO: Move this to + * java/services/com/android/server/BluetoothA2dpService.java * and make the contructor package private again. * @hide */ @@ -34,16 +35,15 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.media.AudioManager; import android.os.Binder; -import android.os.Handler; -import android.os.Message; import android.provider.Settings; import android.util.Log; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; +import java.util.HashMap; +import java.util.Iterator; public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private static final String TAG = "BluetoothA2dpService"; @@ -57,8 +57,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private static final String A2DP_SINK_ADDRESS = "a2dp_sink_address"; private static final String BLUETOOTH_ENABLED = "bluetooth_enabled"; - private static final int MESSAGE_CONNECT_TO = 1; - private final Context mContext; private final IntentFilter mIntentFilter; private HashMap<String, SinkState> mAudioDevices; @@ -88,7 +86,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { mIntentFilter = new IntentFilter(BluetoothIntent.ENABLED_ACTION); mIntentFilter.addAction(BluetoothIntent.DISABLED_ACTION); mIntentFilter.addAction(BluetoothIntent.BOND_STATE_CHANGED_ACTION); - mIntentFilter.addAction(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION); mContext.registerReceiver(mReceiver, mIntentFilter); if (device.isEnabled()) { @@ -126,37 +123,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { setSinkPriority(address, BluetoothA2dp.PRIORITY_OFF); break; } - } else if (action.equals(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION)) { - if (getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF) { - // This device is a preferred sink. Make an A2DP connection - // after a delay. We delay to avoid connection collisions, - // and to give other profiles such as HFP a chance to - // connect first. - Message msg = Message.obtain(mHandler, MESSAGE_CONNECT_TO, address); - mHandler.sendMessageDelayed(msg, 6000); - } - } - } - }; - - private final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MESSAGE_CONNECT_TO: - String address = (String)msg.obj; - // check device is still preferred, and nothing is currently - // connected - if (getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF && - lookupSinksMatchingStates(new int[] { - BluetoothA2dp.STATE_CONNECTING, - BluetoothA2dp.STATE_CONNECTED, - BluetoothA2dp.STATE_PLAYING, - BluetoothA2dp.STATE_DISCONNECTING}).size() == 0) { - log("Auto-connecting A2DP to sink " + address); - connectSink(address); - } - break; } } }; @@ -176,10 +142,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private synchronized void onBluetoothDisable() { if (mAudioDevices != null) { - // copy to allow modification during iteration - String[] paths = new String[mAudioDevices.size()]; - paths = mAudioDevices.keySet().toArray(paths); - for (String path : paths) { + for (String path : mAudioDevices.keySet()) { switch (mAudioDevices.get(path).state) { case BluetoothA2dp.STATE_CONNECTING: case BluetoothA2dp.STATE_CONNECTED: @@ -271,8 +234,17 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { public synchronized List<String> listConnectedSinks() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return lookupSinksMatchingStates(new int[] {BluetoothA2dp.STATE_CONNECTED, - BluetoothA2dp.STATE_PLAYING}); + List<String> connectedSinks = new ArrayList<String>(); + if (mAudioDevices == null) { + return connectedSinks; + } + for (SinkState sink : mAudioDevices.values()) { + if (sink.state == BluetoothA2dp.STATE_CONNECTED || + sink.state == BluetoothA2dp.STATE_PLAYING) { + connectedSinks.add(sink.address); + } + } + return connectedSinks; } public synchronized int getSinkState(String address) { @@ -326,11 +298,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { // bluez 3.36 quietly disconnects the previous sink when a new sink // is connected, so we need to mark all previously connected sinks as // disconnected - - // copy to allow modification during iteration - String[] paths = new String[mAudioDevices.size()]; - paths = mAudioDevices.keySet().toArray(paths); - for (String oldPath : paths) { + for (String oldPath : mAudioDevices.keySet()) { if (path.equals(oldPath)) { continue; } @@ -382,22 +350,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { return null; } - private synchronized List<String> lookupSinksMatchingStates(int[] states) { - List<String> sinks = new ArrayList<String>(); - if (mAudioDevices == null) { - return sinks; - } - for (SinkState sink : mAudioDevices.values()) { - for (int state : states) { - if (sink.state == state) { - sinks.add(sink.address); - break; - } - } - } - return sinks; - } - private synchronized void updateState(String path, int state) { if (mAudioDevices == null) return; diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java index 950ff3a..fa53a60 100644 --- a/core/java/android/server/BluetoothDeviceService.java +++ b/core/java/android/server/BluetoothDeviceService.java @@ -141,20 +141,6 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED, BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); } - - // Remove remoteServiceChannelCallbacks - HashMap<String, IBluetoothDeviceCallback> callbacksMap = - mEventLoop.getRemoteServiceChannelCallbacks(); - IBluetoothDeviceCallback callback; - - for (String address : callbacksMap.keySet()) { - callback = callbacksMap.get(address); - try { - callback.onGetRemoteServiceChannelResult(address, BluetoothError.ERROR_DISABLED); - } catch (RemoteException e) {} - callbacksMap.remove(address); - } - // update mode Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION); intent.putExtra(BluetoothIntent.SCAN_MODE, BluetoothDevice.SCAN_MODE_NONE); @@ -583,18 +569,10 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { } address = address.toUpperCase(); - String[] bonding = mBondState.listInState(BluetoothDevice.BOND_BONDING); - if (bonding.length > 0 && !bonding[0].equals(address)) { - log("Ignoring createBond(): another device is bonding"); - // a different device is currently bonding, fail - return false; - } - // Check for bond state only if we are not performing auto // pairing exponential back-off attempts. if (!mBondState.isAutoPairingAttemptsInProgress(address) && - mBondState.getBondState(address) != BluetoothDevice.BOND_NOT_BONDED) { - log("Ignoring createBond(): this device is already bonding or bonded"); + mBondState.getBondState(address) != BluetoothDevice.BOND_NOT_BONDED) { return false; } diff --git a/core/java/android/server/search/SearchableInfo.java b/core/java/android/server/search/SearchableInfo.java index 0c04839..c18675e 100644 --- a/core/java/android/server/search/SearchableInfo.java +++ b/core/java/android/server/search/SearchableInfo.java @@ -35,7 +35,6 @@ import android.text.InputType; import android.util.AttributeSet; import android.util.Log; import android.util.Xml; -import android.view.inputmethod.EditorInfo; import java.io.IOException; import java.util.ArrayList; @@ -78,7 +77,6 @@ public final class SearchableInfo implements Parcelable { private int mIconId = 0; private int mSearchButtonText = 0; private int mSearchInputType = 0; - private int mSearchImeOptions = 0; private String mSuggestAuthority = null; private String mSuggestPath = null; private String mSuggestSelection = null; @@ -431,9 +429,8 @@ public final class SearchableInfo implements Parcelable { com.android.internal.R.styleable.Searchable_searchButtonText, 0); mSearchInputType = a.getInt(com.android.internal.R.styleable.Searchable_inputType, InputType.TYPE_CLASS_TEXT | + InputType.TYPE_TEXT_FLAG_SEARCH | InputType.TYPE_TEXT_VARIATION_NORMAL); - mSearchImeOptions = a.getInt(com.android.internal.R.styleable.Searchable_imeOptions, - EditorInfo.IME_ACTION_SEARCH); setSearchModeFlags(); if (DBG_INHIBIT_SUGGESTIONS == 0) { @@ -746,17 +743,6 @@ public final class SearchableInfo implements Parcelable { } /** - * Return the input method options specified in the searchable attributes. - * This will default to EditorInfo.ACTION_SEARCH if not specified (which is - * appropriate for a search box). - * - * @return the input type - */ - public int getImeOptions() { - return mSearchImeOptions; - } - - /** * Return the list of searchable activities, for use in the drop-down. */ public static ArrayList<SearchableInfo> getSearchablesList() { @@ -796,7 +782,6 @@ public final class SearchableInfo implements Parcelable { mIconId = in.readInt(); mSearchButtonText = in.readInt(); mSearchInputType = in.readInt(); - mSearchImeOptions = in.readInt(); setSearchModeFlags(); mSuggestAuthority = in.readString(); @@ -833,7 +818,6 @@ public final class SearchableInfo implements Parcelable { dest.writeInt(mIconId); dest.writeInt(mSearchButtonText); dest.writeInt(mSearchInputType); - dest.writeInt(mSearchImeOptions); dest.writeString(mSuggestAuthority); dest.writeString(mSuggestPath); diff --git a/core/java/android/speech/srec/package.html b/core/java/android/speech/srec/package.html index 9a99df8..723b30b 100644 --- a/core/java/android/speech/srec/package.html +++ b/core/java/android/speech/srec/package.html @@ -1,6 +1,5 @@ <HTML> <BODY> Simple, synchronous SREC speech recognition API. -@hide </BODY> </HTML> diff --git a/core/java/android/text/InputType.java b/core/java/android/text/InputType.java index f643f92..a073cf4 100644 --- a/core/java/android/text/InputType.java +++ b/core/java/android/text/InputType.java @@ -128,6 +128,11 @@ public interface InputType { */ public static final int TYPE_TEXT_FLAG_IME_MULTI_LINE = 0x00040000; + /** + * Flag for {@link #TYPE_CLASS_TEXT}: flags any text being used as a search string + */ + public static final int TYPE_TEXT_FLAG_SEARCH = 0x00080000; + // ---------------------------------------------------------------------- /** diff --git a/core/java/android/text/Styled.java b/core/java/android/text/Styled.java index 0aa2004..05c27ea 100644 --- a/core/java/android/text/Styled.java +++ b/core/java/android/text/Styled.java @@ -16,26 +16,25 @@ package android.text; -import android.graphics.Canvas; import android.graphics.Paint; -import android.text.style.CharacterStyle; -import android.text.style.MetricAffectingSpan; -import android.text.style.ReplacementSpan; - -/** - * This class provides static methods for drawing and measuring styled texts, like - * {@link android.text.Spanned} object with {@link android.text.style.ReplacementSpan}. - * @hide - */ -public class Styled +import android.graphics.Canvas; +import android.graphics.Path; +import android.graphics.RectF; +import android.graphics.Typeface; +import android.graphics.MaskFilter; +import android.graphics.Rasterizer; +import android.graphics.LayerRasterizer; +import android.text.style.*; + +/* package */ class Styled { private static float each(Canvas canvas, Spanned text, int start, int end, int dir, boolean reverse, float x, int top, int y, int bottom, - Paint.FontMetricsInt fmi, + Paint.FontMetricsInt fm, + TextPaint realPaint, TextPaint paint, - TextPaint workPaint, boolean needwid) { boolean havewid = false; @@ -44,9 +43,9 @@ public class Styled ReplacementSpan replacement = null; - paint.bgColor = 0; - paint.baselineShift = 0; - workPaint.set(paint); + realPaint.bgColor = 0; + realPaint.baselineShift = 0; + paint.set(realPaint); if (spans.length > 0) { for (int i = 0; i < spans.length; i++) { @@ -56,7 +55,7 @@ public class Styled replacement = (ReplacementSpan)span; } else { - span.updateDrawState(workPaint); + span.updateDrawState(paint); } } } @@ -75,66 +74,66 @@ public class Styled tmpend = end; } - if (fmi != null) { - workPaint.getFontMetricsInt(fmi); + if (fm != null) { + paint.getFontMetricsInt(fm); } if (canvas != null) { - if (workPaint.bgColor != 0) { - int c = workPaint.getColor(); - Paint.Style s = workPaint.getStyle(); - workPaint.setColor(workPaint.bgColor); - workPaint.setStyle(Paint.Style.FILL); + if (paint.bgColor != 0) { + int c = paint.getColor(); + Paint.Style s = paint.getStyle(); + paint.setColor(paint.bgColor); + paint.setStyle(Paint.Style.FILL); if (!havewid) { - ret = workPaint.measureText(tmp, tmpstart, tmpend); + ret = paint.measureText(tmp, tmpstart, tmpend); havewid = true; } if (dir == Layout.DIR_RIGHT_TO_LEFT) - canvas.drawRect(x - ret, top, x, bottom, workPaint); + canvas.drawRect(x - ret, top, x, bottom, paint); else - canvas.drawRect(x, top, x + ret, bottom, workPaint); + canvas.drawRect(x, top, x + ret, bottom, paint); - workPaint.setStyle(s); - workPaint.setColor(c); + paint.setStyle(s); + paint.setColor(c); } if (dir == Layout.DIR_RIGHT_TO_LEFT) { if (!havewid) { - ret = workPaint.measureText(tmp, tmpstart, tmpend); + ret = paint.measureText(tmp, tmpstart, tmpend); havewid = true; } canvas.drawText(tmp, tmpstart, tmpend, - x - ret, y + workPaint.baselineShift, workPaint); + x - ret, y + paint.baselineShift, paint); } else { if (needwid) { if (!havewid) { - ret = workPaint.measureText(tmp, tmpstart, tmpend); + ret = paint.measureText(tmp, tmpstart, tmpend); havewid = true; } } canvas.drawText(tmp, tmpstart, tmpend, - x, y + workPaint.baselineShift, workPaint); + x, y + paint.baselineShift, paint); } } else { if (needwid && !havewid) { - ret = workPaint.measureText(tmp, tmpstart, tmpend); + ret = paint.measureText(tmp, tmpstart, tmpend); havewid = true; } } } else { - ret = replacement.getSize(workPaint, text, start, end, fmi); + ret = replacement.getSize(paint, text, start, end, fm); if (canvas != null) { if (dir == Layout.DIR_RIGHT_TO_LEFT) replacement.draw(canvas, text, start, end, - x - ret, top, y, bottom, workPaint); + x - ret, top, y, bottom, paint); else replacement.draw(canvas, text, start, end, - x, top, y, bottom, workPaint); + x, top, y, bottom, paint); } } @@ -144,29 +143,15 @@ public class Styled return ret; } - /** - * Return the advance widths for the characters in the string. - * See also {@link android.graphics.Paint#getTextWidths(CharSequence, int, int, float[])}. - * - * @param paint The main {@link TextPaint} object. - * @param workPaint The {@link TextPaint} object used for temporal workspace. - * @param text The text to measure - * @param start The index of the first char to to measure - * @param end The end of the text slice to measure - * @param widths Array to receive the advance widths of the characters. - * Must be at least a large as (end - start). - * @param fmi FontMetrics information. Can be null. - * @return The actual number of widths returned. - */ - public static int getTextWidths(TextPaint paint, - TextPaint workPaint, - Spanned text, int start, int end, - float[] widths, Paint.FontMetricsInt fmi) { - // Keep workPaint as is so that developers reuse the workspace. + public static int getTextWidths(TextPaint realPaint, + TextPaint paint, + Spanned text, int start, int end, + float[] widths, Paint.FontMetricsInt fm) { + MetricAffectingSpan[] spans = text.getSpans(start, end, MetricAffectingSpan.class); ReplacementSpan replacement = null; - workPaint.set(paint); + paint.set(realPaint); for (int i = 0; i < spans.length; i++) { MetricAffectingSpan span = spans[i]; @@ -174,15 +159,15 @@ public class Styled replacement = (ReplacementSpan)span; } else { - span.updateMeasureState(workPaint); + span.updateMeasureState(paint); } } if (replacement == null) { - workPaint.getFontMetricsInt(fmi); - workPaint.getTextWidths(text, start, end, widths); + paint.getFontMetricsInt(fm); + paint.getTextWidths(text, start, end, widths); } else { - int wid = replacement.getSize(workPaint, text, start, end, fmi); + int wid = replacement.getSize(paint, text, start, end, fm); if (end > start) { widths[0] = wid; @@ -198,10 +183,10 @@ public class Styled CharSequence text, int start, int end, int dir, boolean reverse, float x, int top, int y, int bottom, - Paint.FontMetricsInt fmi, + Paint.FontMetricsInt fm, TextPaint paint, TextPaint workPaint, - boolean needWidth) { + boolean needwid) { if (! (text instanceof Spanned)) { float ret = 0; @@ -209,22 +194,22 @@ public class Styled CharSequence tmp = TextUtils.getReverse(text, start, end); int tmpend = end - start; - if (canvas != null || needWidth) + if (canvas != null || needwid) ret = paint.measureText(tmp, 0, tmpend); if (canvas != null) canvas.drawText(tmp, 0, tmpend, x - ret, y, paint); } else { - if (needWidth) + if (needwid) ret = paint.measureText(text, start, end); if (canvas != null) canvas.drawText(text, start, end, x, y, paint); } - if (fmi != null) { - paint.getFontMetricsInt(fmi); + if (fm != null) { + paint.getFontMetricsInt(fm); } return ret * dir; //Layout.DIR_RIGHT_TO_LEFT == -1 @@ -247,129 +232,67 @@ public class Styled next = sp.nextSpanTransition(i, end, division); x += each(canvas, sp, i, next, dir, reverse, - x, top, y, bottom, fmi, paint, workPaint, - needWidth || next != end); - - if (fmi != null) { - if (fmi.ascent < asc) - asc = fmi.ascent; - if (fmi.descent > desc) - desc = fmi.descent; - - if (fmi.top < ftop) - ftop = fmi.top; - if (fmi.bottom > fbot) - fbot = fmi.bottom; + x, top, y, bottom, fm, paint, workPaint, + needwid || next != end); + + if (fm != null) { + if (fm.ascent < asc) + asc = fm.ascent; + if (fm.descent > desc) + desc = fm.descent; + + if (fm.top < ftop) + ftop = fm.top; + if (fm.bottom > fbot) + fbot = fm.bottom; } } - if (fmi != null) { + if (fm != null) { if (start == end) { - paint.getFontMetricsInt(fmi); + paint.getFontMetricsInt(fm); } else { - fmi.ascent = asc; - fmi.descent = desc; - fmi.top = ftop; - fmi.bottom = fbot; + fm.ascent = asc; + fm.descent = desc; + fm.top = ftop; + fm.bottom = fbot; } } return x - ox; } - - /* package */ static float drawText(Canvas canvas, - CharSequence text, int start, int end, - int direction, boolean reverse, - float x, int top, int y, int bottom, - TextPaint paint, - TextPaint workPaint, - boolean needWidth) { - if ((direction == Layout.DIR_RIGHT_TO_LEFT && !reverse) || - (reverse && direction == Layout.DIR_LEFT_TO_RIGHT)) { + public static float drawText(Canvas canvas, + CharSequence text, int start, int end, + int dir, boolean reverse, + float x, int top, int y, int bottom, + TextPaint paint, + TextPaint workPaint, + boolean needwid) { + if ((dir == Layout.DIR_RIGHT_TO_LEFT && !reverse)||(reverse && dir == Layout.DIR_LEFT_TO_RIGHT)) { float ch = foreach(null, text, start, end, Layout.DIR_LEFT_TO_RIGHT, false, 0, 0, 0, 0, null, paint, workPaint, true); - ch *= direction; // DIR_RIGHT_TO_LEFT == -1 - foreach(canvas, text, start, end, -direction, + ch *= dir; // DIR_RIGHT_TO_LEFT == -1 + foreach(canvas, text, start, end, -dir, reverse, x + ch, top, y, bottom, null, paint, workPaint, true); return ch; } - return foreach(canvas, text, start, end, direction, reverse, + return foreach(canvas, text, start, end, dir, reverse, x, top, y, bottom, null, paint, workPaint, - needWidth); - } - - /** - * Draw the specified range of text, specified by start/end, with its origin at (x,y), - * in the specified Paint. The origin is interpreted based on the Align setting in the - * Paint. - * - * This method considers style information in the text - * (e.g. Even when text is an instance of {@link android.text.Spanned}, this method - * correctly draws the text). - * See also - * {@link android.graphics.Canvas#drawText(CharSequence, int, int, float, float, Paint)} - * and - * {@link android.graphics.Canvas#drawRect(float, float, float, float, Paint)}. - * - * @param canvas The target canvas. - * @param text The text to be drawn - * @param start The index of the first character in text to draw - * @param end (end - 1) is the index of the last character in text to draw - * @param direction The direction of the text. This must be - * {@link android.text.Layout#DIR_LEFT_TO_RIGHT} or - * {@link android.text.Layout#DIR_RIGHT_TO_LEFT}. - * @param x The x-coordinate of origin for where to draw the text - * @param top The top side of the rectangle to be drawn - * @param y The y-coordinate of origin for where to draw the text - * @param bottom The bottom side of the rectangle to be drawn - * @param paint The main {@link TextPaint} object. - * @param workPaint The {@link TextPaint} object used for temporal workspace. - * @param needWidth If true, this method returns the width of drawn text. - * @return Width of the drawn text if needWidth is true. - */ - public static float drawText(Canvas canvas, - CharSequence text, int start, int end, - int direction, - float x, int top, int y, int bottom, - TextPaint paint, - TextPaint workPaint, - boolean needWidth) { - // For safety. - direction = direction >= 0 ? Layout.DIR_LEFT_TO_RIGHT : Layout.DIR_RIGHT_TO_LEFT; - /* - * Hided "reverse" parameter since it is meaningless for external developers. - * Kept workPaint as is so that developers reuse the workspace. - */ - return drawText(canvas, text, start, end, direction, false, - x, top, y, bottom, paint, workPaint, needWidth); + needwid); } - - /** - * Return the width of the text, considering style information in the text - * (e.g. Even when text is an instance of {@link android.text.Spanned}, this method - * correctly mesures the width of the text). - * - * @param paint The main {@link TextPaint} object. - * @param workPaint The {@link TextPaint} object used for temporal workspace. - * @param text The text to measure - * @param start The index of the first character to start measuring - * @param end 1 beyond the index of the last character to measure - * @param fmi FontMetrics information. Can be null - * @return The width of the text - */ + public static float measureText(TextPaint paint, TextPaint workPaint, CharSequence text, int start, int end, - Paint.FontMetricsInt fmi) { - // Keep workPaint as is so that developers reuse the workspace. + Paint.FontMetricsInt fm) { return foreach(null, text, start, end, Layout.DIR_LEFT_TO_RIGHT, false, - 0, 0, 0, 0, fmi, paint, workPaint, true); + 0, 0, 0, 0, fm, paint, workPaint, true); } } diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java index 8a7cdd9..feae6cf 100644 --- a/core/java/android/text/format/DateUtils.java +++ b/core/java/android/text/format/DateUtils.java @@ -595,17 +595,6 @@ public class DateUtils * @param elapsedSeconds the elapsed time in seconds. */ public static String formatElapsedTime(long elapsedSeconds) { - return formatElapsedTime(null, elapsedSeconds); - } - - /** - * Formats an elapsed time in the form "MM:SS" or "H:MM:SS" - * for display on the call-in-progress screen. - * - * @param recycle {@link StringBuilder} to recycle, if possible - * @param elapsedSeconds the elapsed time in seconds. - */ - public static String formatElapsedTime(StringBuilder recycle, long elapsedSeconds) { initFormatStrings(); long hours = 0; @@ -624,24 +613,18 @@ public class DateUtils String result; if (hours > 0) { - return formatElapsedTime(recycle, sElapsedFormatHMMSS, hours, minutes, seconds); + return formatElapsedTime(sElapsedFormatHMMSS, hours, minutes, seconds); } else { - return formatElapsedTime(recycle, sElapsedFormatMMSS, minutes, seconds); + return formatElapsedTime(sElapsedFormatMMSS, minutes, seconds); } } /** * Fast formatting of h:mm:ss */ - private static String formatElapsedTime(StringBuilder recycle, String format, long hours, - long minutes, long seconds) { + private static String formatElapsedTime(String format, long hours, long minutes, long seconds) { if (FAST_FORMAT_HMMSS.equals(format)) { - StringBuilder sb = recycle; - if (sb == null) { - sb = new StringBuilder(8); - } else { - sb.setLength(0); - } + StringBuffer sb = new StringBuffer(16); sb.append(hours); sb.append(TIME_SEPARATOR); if (minutes < 10) { @@ -666,15 +649,9 @@ public class DateUtils /** * Fast formatting of m:ss */ - private static String formatElapsedTime(StringBuilder recycle, String format, long minutes, - long seconds) { + private static String formatElapsedTime(String format, long minutes, long seconds) { if (FAST_FORMAT_MMSS.equals(format)) { - StringBuilder sb = recycle; - if (sb == null) { - sb = new StringBuilder(8); - } else { - sb.setLength(0); - } + StringBuffer sb = new StringBuffer(16); if (minutes < 10) { sb.append(TIME_PADDING); } else { @@ -1051,9 +1028,8 @@ public class DateUtils * If FORMAT_NO_YEAR is set, then the year is not shown. * If neither FORMAT_SHOW_YEAR nor FORMAT_NO_YEAR are set, then the year * is shown only if it is different from the current year, or if the start - * and end dates fall on different years. If both are set, - * FORMAT_SHOW_YEAR takes precedence. - * + * and end dates fall on different years. + * * <p> * Normally the date is shown unless the start and end day are the same. * If FORMAT_SHOW_DATE is set, then the date is always shown, even for @@ -1144,28 +1120,24 @@ public class DateUtils boolean abbrevMonth = (flags & (FORMAT_ABBREV_MONTH | FORMAT_ABBREV_ALL)) != 0; boolean noMonthDay = (flags & FORMAT_NO_MONTH_DAY) != 0; boolean numericDate = (flags & FORMAT_NUMERIC_DATE) != 0; - - // If we're getting called with a single instant in time (from - // e.g. formatDateTime(), below), then we can skip a lot of - // computation below that'd otherwise be thrown out. - boolean isInstant = (startMillis == endMillis); - - Time startDate = useUTC ? new Time(Time.TIMEZONE_UTC) : new Time(); - startDate.set(startMillis); - + + Time startDate; Time endDate; - int dayDistance; - if (isInstant) { - endDate = startDate; - dayDistance = 0; + + if (useUTC) { + startDate = new Time(Time.TIMEZONE_UTC); + endDate = new Time(Time.TIMEZONE_UTC); } else { - endDate = useUTC ? new Time(Time.TIMEZONE_UTC) : new Time(); - endDate.set(endMillis); - int startJulianDay = Time.getJulianDay(startMillis, startDate.gmtoff); - int endJulianDay = Time.getJulianDay(endMillis, endDate.gmtoff); - dayDistance = endJulianDay - startJulianDay; + startDate = new Time(); + endDate = new Time(); } - + + startDate.set(startMillis); + endDate.set(endMillis); + int startJulianDay = Time.getJulianDay(startMillis, startDate.gmtoff); + int endJulianDay = Time.getJulianDay(endMillis, endDate.gmtoff); + int dayDistance = endJulianDay - startJulianDay; + // If the end date ends at 12am at the beginning of a day, // then modify it to make it look like it ends at midnight on // the previous day. This will allow us to display "8pm - midnight", @@ -1180,21 +1152,20 @@ public class DateUtils // and an end date of Nov 12 at 00:00. // If the start and end time are the same, then skip this and don't // adjust the date. - if (!isInstant - && (endDate.hour | endDate.minute | endDate.second) == 0 - && (!showTime || dayDistance <= 1)) { + if ((endDate.hour | endDate.minute | endDate.second) == 0 + && (!showTime || dayDistance <= 1) && (startMillis != endMillis)) { endDate.monthDay -= 1; endDate.normalize(true /* ignore isDst */); } - + int startDay = startDate.monthDay; int startMonthNum = startDate.month; int startYear = startDate.year; - + int endDay = endDate.monthDay; int endMonthNum = endDate.month; int endYear = endDate.year; - + String startWeekDayString = ""; String endWeekDayString = ""; if (showWeekDay) { @@ -1205,9 +1176,9 @@ public class DateUtils weekDayFormat = WEEKDAY_FORMAT; } startWeekDayString = startDate.format(weekDayFormat); - endWeekDayString = isInstant ? startWeekDayString : endDate.format(weekDayFormat); + endWeekDayString = endDate.format(weekDayFormat); } - + String startTimeString = ""; String endTimeString = ""; if (showTime) { @@ -1233,7 +1204,7 @@ public class DateUtils boolean capNoon = (flags & FORMAT_CAP_NOON) != 0; boolean noMidnight = (flags & FORMAT_NO_MIDNIGHT) != 0; boolean capMidnight = (flags & FORMAT_CAP_MIDNIGHT) != 0; - + boolean startOnTheHour = startDate.minute == 0 && startDate.second == 0; boolean endOnTheHour = endDate.minute == 0 && endDate.second == 0; if (abbrevTime && startOnTheHour) { @@ -1249,41 +1220,20 @@ public class DateUtils startTimeFormat = res.getString(com.android.internal.R.string.hour_minute_ampm); } } - - // Don't waste time on setting endTimeFormat when - // we're dealing with an instant, where we'll never - // need the end point. (It's the same as the start - // point) - if (!isInstant) { - if (abbrevTime && endOnTheHour) { - if (capAMPM) { - endTimeFormat = res.getString(com.android.internal.R.string.hour_cap_ampm); - } else { - endTimeFormat = res.getString(com.android.internal.R.string.hour_ampm); - } + if (abbrevTime && endOnTheHour) { + if (capAMPM) { + endTimeFormat = res.getString(com.android.internal.R.string.hour_cap_ampm); } else { - if (capAMPM) { - endTimeFormat = res.getString(com.android.internal.R.string.hour_minute_cap_ampm); - } else { - endTimeFormat = res.getString(com.android.internal.R.string.hour_minute_ampm); - } + endTimeFormat = res.getString(com.android.internal.R.string.hour_ampm); } - - if (endDate.hour == 12 && endOnTheHour && !noNoon) { - if (capNoon) { - endTimeFormat = res.getString(com.android.internal.R.string.Noon); - } else { - endTimeFormat = res.getString(com.android.internal.R.string.noon); - } - } else if (endDate.hour == 0 && endOnTheHour && !noMidnight) { - if (capMidnight) { - endTimeFormat = res.getString(com.android.internal.R.string.Midnight); - } else { - endTimeFormat = res.getString(com.android.internal.R.string.midnight); - } + } else { + if (capAMPM) { + endTimeFormat = res.getString(com.android.internal.R.string.hour_minute_cap_ampm); + } else { + endTimeFormat = res.getString(com.android.internal.R.string.hour_minute_ampm); } } - + if (startDate.hour == 12 && startOnTheHour && !noNoon) { if (capNoon) { startTimeFormat = res.getString(com.android.internal.R.string.Noon); @@ -1293,32 +1243,37 @@ public class DateUtils // Don't show the start time starting at midnight. Show // 12am instead. } + + if (endDate.hour == 12 && endOnTheHour && !noNoon) { + if (capNoon) { + endTimeFormat = res.getString(com.android.internal.R.string.Noon); + } else { + endTimeFormat = res.getString(com.android.internal.R.string.noon); + } + } else if (endDate.hour == 0 && endOnTheHour && !noMidnight) { + if (capMidnight) { + endTimeFormat = res.getString(com.android.internal.R.string.Midnight); + } else { + endTimeFormat = res.getString(com.android.internal.R.string.midnight); + } + } } - startTimeString = startDate.format(startTimeFormat); - endTimeString = isInstant ? startTimeString : endDate.format(endTimeFormat); + endTimeString = endDate.format(endTimeFormat); } - + + // Get the current year + long millis = System.currentTimeMillis(); + Time time = new Time(); + time.set(millis); + int currentYear = time.year; + // Show the year if the user specified FORMAT_SHOW_YEAR or if // the starting and end years are different from each other // or from the current year. But don't show the year if the - // user specified FORMAT_NO_YEAR. - if (showYear) { - // No code... just a comment for clarity. Keep showYear - // on, as they enabled it with FORMAT_SHOW_YEAR. This - // takes precedence over them setting FORMAT_NO_YEAR. - } else if (noYear) { - // They explicitly didn't want a year. - showYear = false; - } else if (startYear != endYear) { - showYear = true; - } else { - // Show the year if it's not equal to the current year. - Time currentTime = new Time(); - currentTime.setToNow(); - showYear = startYear != currentTime.year; - } - + // user specified FORMAT_NO_YEAR; + showYear = showYear || (!noYear && (startYear != endYear || startYear != currentYear)); + String defaultDateFormat, fullFormat, dateRange; if (numericDate) { defaultDateFormat = res.getString(com.android.internal.R.string.numeric_date); @@ -1351,7 +1306,7 @@ public class DateUtils } } } - + if (showWeekDay) { if (showTime) { fullFormat = res.getString(com.android.internal.R.string.wday1_date1_time1_wday2_date2_time2); @@ -1365,20 +1320,20 @@ public class DateUtils fullFormat = res.getString(com.android.internal.R.string.date1_date2); } } - + if (noMonthDay && startMonthNum == endMonthNum) { // Example: "January, 2008" String startDateString = startDate.format(defaultDateFormat); return startDateString; } - + if (startYear != endYear || noMonthDay) { // Different year or we are not showing the month day number. // Example: "December 31, 2007 - January 1, 2008" // Or: "January - February, 2008" String startDateString = startDate.format(defaultDateFormat); String endDateString = endDate.format(defaultDateFormat); - + // The values that are used in a fullFormat string are specified // by position. dateRange = String.format(fullFormat, @@ -1386,7 +1341,7 @@ public class DateUtils endWeekDayString, endDateString, endTimeString); return dateRange; } - + // Get the month, day, and year strings for the start and end dates String monthFormat; if (numericDate) { @@ -1399,17 +1354,16 @@ public class DateUtils String startMonthString = startDate.format(monthFormat); String startMonthDayString = startDate.format(MONTH_DAY_FORMAT); String startYearString = startDate.format(YEAR_FORMAT); - - String endMonthString = isInstant ? null : endDate.format(monthFormat); - String endMonthDayString = isInstant ? null : endDate.format(MONTH_DAY_FORMAT); - String endYearString = isInstant ? null : endDate.format(YEAR_FORMAT); - + String endMonthString = endDate.format(monthFormat); + String endMonthDayString = endDate.format(MONTH_DAY_FORMAT); + String endYearString = endDate.format(YEAR_FORMAT); + if (startMonthNum != endMonthNum) { // Same year, different month. // Example: "October 28 - November 3" // or: "Wed, Oct 31 - Sat, Nov 3, 2007" // or: "Oct 31, 8am - Sat, Nov 3, 2007, 5pm" - + int index = 0; if (showWeekDay) index = 1; if (showYear) index += 2; @@ -1417,7 +1371,7 @@ public class DateUtils if (numericDate) index += 8; int resId = sameYearTable[index]; fullFormat = res.getString(resId); - + // The values that are used in a fullFormat string are specified // by position. dateRange = String.format(fullFormat, @@ -1427,7 +1381,7 @@ public class DateUtils endYearString, endTimeString); return dateRange; } - + if (startDay != endDay) { // Same month, different day. int index = 0; @@ -1437,7 +1391,7 @@ public class DateUtils if (numericDate) index += 8; int resId = sameMonthTable[index]; fullFormat = res.getString(resId); - + // The values that are used in a fullFormat string are specified // by position. dateRange = String.format(fullFormat, @@ -1447,19 +1401,19 @@ public class DateUtils endYearString, endTimeString); return dateRange; } - + // Same start and end day boolean showDate = (flags & FORMAT_SHOW_DATE) != 0; - + // If nothing was specified, then show the date. if (!showTime && !showDate && !showWeekDay) showDate = true; - + // Compute the time string (example: "10:00 - 11:00 am") String timeString = ""; if (showTime) { // If the start and end time are the same, then just show the // start time. - if (isInstant) { + if (startMillis == endMillis) { // Same start and end time. // Example: "10:15 AM" timeString = startTimeString; @@ -1469,7 +1423,7 @@ public class DateUtils timeString = String.format(timeFormat, startTimeString, endTimeString); } } - + // Figure out which full format to use. fullFormat = ""; String dateString = ""; @@ -1503,7 +1457,7 @@ public class DateUtils } else if (showTime) { return timeString; } - + // The values that are used in a fullFormat string are specified // by position. dateRange = String.format(fullFormat, timeString, startWeekDayString, dateString); diff --git a/core/java/android/text/method/NumberKeyListener.java b/core/java/android/text/method/NumberKeyListener.java index 9270ca5..e500fae 100644 --- a/core/java/android/text/method/NumberKeyListener.java +++ b/core/java/android/text/method/NumberKeyListener.java @@ -101,11 +101,6 @@ public abstract class NumberKeyListener extends BaseKeyListener selEnd = Math.max(a, b); } - if (selStart < 0 || selEnd < 0) { - selStart = selEnd = 0; - Selection.setSelection(content, 0); - } - int i = event != null ? lookup(event, content) : 0; int repeatCount = event != null ? event.getRepeatCount() : 0; if (repeatCount == 0) { diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java index e0231a7..679c683 100644 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -113,48 +113,20 @@ public class GestureDetector { } /** - * The listener that is used to notify when a double-tap or a confirmed - * single-tap occur. + * @hide pending API council */ public interface OnDoubleTapListener { - /** - * Notified when a single-tap occurs. - * <p> - * Unlike {@link OnGestureListener#onSingleTapUp(MotionEvent)}, this - * will only be called after the detector is confident that the user's - * first tap is not followed by a second tap leading to a double-tap - * gesture. - * - * @param e The down motion event of the single-tap. - * @return true if the event is consumed, else false - */ boolean onSingleTapConfirmed(MotionEvent e); - - /** - * Notified when a double-tap occurs. - * - * @param e The down motion event of the first tap of the double-tap. - * @return true if the event is consumed, else false - */ - boolean onDoubleTap(MotionEvent e); - - /** - * Notified when an event within a double-tap gesture occurs, including - * the down, move, and up events. - * - * @param e The motion event that occurred during the double-tap gesture. - * @return true if the event is consumed, else false - */ boolean onDoubleTapEvent(MotionEvent e); } - + /** - * A convenience class to extend when you only want to listen for a subset - * of all the gestures. This implements all methods in the - * {@link OnGestureListener} and {@link OnDoubleTapListener} but does - * nothing and return {@code false} for all applicable methods. + * A convenience class to extend when you only want to listen for a + * subset of all the gestures. This implements all methods in the + * {@link OnGestureListener} but does nothing and return {@code false} + * for all applicable methods. */ - public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener { + public static class SimpleOnGestureListener implements OnGestureListener { public boolean onSingleTapUp(MotionEvent e) { return false; } @@ -178,25 +150,13 @@ public class GestureDetector { public boolean onDown(MotionEvent e) { return false; } - - public boolean onDoubleTap(MotionEvent e) { - return false; - } - - public boolean onDoubleTapEvent(MotionEvent e) { - return false; - } - - public boolean onSingleTapConfirmed(MotionEvent e) { - return false; - } } // TODO: ViewConfiguration private int mBiggerTouchSlopSquare = 20 * 20; - + private int mTouchSlopSquare; - private int mDoubleTapSlopSquare; + private int mDoubleTapSlopSquare; private int mMinimumFlingVelocity; private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout(); @@ -204,7 +164,7 @@ public class GestureDetector { // TODO make new double-tap timeout, and define its events (i.e. either time // between down-down or time between up-down) private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout(); - + // constants for Message.what used by GestureHandler below private static final int SHOW_PRESS = 1; private static final int LONG_PRESS = 2; @@ -221,13 +181,13 @@ public class GestureDetector { private MotionEvent mCurrentDownEvent; private MotionEvent mPreviousUpEvent; - + /** * True when the user is still touching for the second tap (down, move, and * up events). Can only be true if there is a double tap listener attached. */ private boolean mIsDoubleTapping; - + private float mLastMotionY; private float mLastMotionX; @@ -266,7 +226,7 @@ public class GestureDetector { break; default: - throw new RuntimeException("Unknown message " + msg); //never + throw new RuntimeException("Unknown message " + msg); //never } } } @@ -343,9 +303,6 @@ public class GestureDetector { mHandler = new GestureHandler(); } mListener = listener; - if (listener instanceof OnDoubleTapListener) { - setOnDoubleTapListener((OnDoubleTapListener) listener); - } init(context); } @@ -374,11 +331,8 @@ public class GestureDetector { } /** - * Sets the listener which will be called for double-tap and related - * gestures. - * - * @param onDoubleTapListener the listener invoked for all the callbacks, or - * null to stop listening for double-tap gestures. + * @hide pending API council + * @param onDoubleTapListener */ public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) { mDoubleTapListener = onDoubleTapListener; @@ -433,10 +387,7 @@ public class GestureDetector { isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) { // This is a second tap mIsDoubleTapping = true; - // Give a callback with the first tap of the double-tap - handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent); - // Give a callback with down event of the double-tap - handled |= mDoubleTapListener.onDoubleTapEvent(ev); + handled = mDoubleTapListener.onDoubleTapEvent(ev); } else { // This is a first tap mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT); @@ -467,8 +418,7 @@ public class GestureDetector { final float scrollX = mLastMotionX - x; final float scrollY = mLastMotionY - y; if (mIsDoubleTapping) { - // Give the move events of the double-tap - handled |= mDoubleTapListener.onDoubleTapEvent(ev); + handled = mDoubleTapListener.onDoubleTapEvent(ev); } else if (mAlwaysInTapRegion) { final int deltaX = (int) (x - mCurrentDownEvent.getX()); final int deltaY = (int) (y - mCurrentDownEvent.getY()); @@ -496,8 +446,7 @@ public class GestureDetector { mStillDown = false; MotionEvent currentUpEvent = MotionEvent.obtain(ev); if (mIsDoubleTapping) { - // Finally, give the up event of the double-tap - handled |= mDoubleTapListener.onDoubleTapEvent(ev); + handled = mDoubleTapListener.onDoubleTapEvent(ev); mIsDoubleTapping = false; break; } else if (mInLongPress) { @@ -546,7 +495,7 @@ public class GestureDetector { if (!mAlwaysInBiggerTapRegion) { return false; } - + if (secondDown.getEventTime() - firstUp.getEventTime() > DOUBLE_TAP_TIMEOUT) { return false; } @@ -555,7 +504,7 @@ public class GestureDetector { int deltaY = (int) firstDown.getY() - (int) secondDown.getY(); return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare); } - + private void dispatchLongPress() { mHandler.removeMessages(TAP); mInLongPress = true; diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 430cc71..d6ea91c 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -229,12 +229,6 @@ public class KeyEvent implements Parcelable { public static final int FLAG_SOFT_KEYBOARD = 0x2; /** - * This mask is set if we don't want the key event to cause us to leave - * touch mode. - */ - public static final int FLAG_KEEP_TOUCH_MODE = 0x4; - - /** * Returns the maximum keycode. */ public static int getMaxKeyCode() { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 406fad8..5ed3a7e 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -57,7 +57,6 @@ import com.android.internal.view.menu.MenuBuilder; import java.util.ArrayList; import java.util.Arrays; -import java.lang.ref.SoftReference; /** * <p> @@ -1564,7 +1563,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { private int[] mDrawableState = null; - private SoftReference<Bitmap> mDrawingCache; + private Bitmap mDrawingCache; /** * When this view has focus and the next focus is {@link #FOCUS_LEFT}, @@ -3951,16 +3950,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } if ((changed & WILL_NOT_CACHE_DRAWING) != 0) { - destroyDrawingCache(); + if (mDrawingCache != null) { + mDrawingCache.recycle(); + } + mDrawingCache = null; } if ((changed & DRAWING_CACHE_ENABLED) != 0) { - destroyDrawingCache(); + if (mDrawingCache != null) { + mDrawingCache.recycle(); + } + mDrawingCache = null; mPrivateFlags &= ~DRAWING_CACHE_VALID; } if ((changed & DRAWING_CACHE_QUALITY_MASK) != 0) { - destroyDrawingCache(); + if (mDrawingCache != null) { + mDrawingCache.recycle(); + } + mDrawingCache = null; mPrivateFlags &= ~DRAWING_CACHE_VALID; } @@ -5407,10 +5415,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback { if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) { return null; } - if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) { + if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED && + ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || mDrawingCache == null)) { buildDrawingCache(); } - return mDrawingCache == null ? null : mDrawingCache.get(); + return mDrawingCache; } /** @@ -5425,8 +5434,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { */ public void destroyDrawingCache() { if (mDrawingCache != null) { - final Bitmap bitmap = mDrawingCache.get(); - if (bitmap != null) bitmap.recycle(); + mDrawingCache.recycle(); mDrawingCache = null; } } @@ -5466,9 +5474,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * @see #destroyDrawingCache() */ public void buildDrawingCache() { - if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || mDrawingCache == null || - mDrawingCache.get() == null) { - + if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || mDrawingCache == null) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE); } @@ -5486,12 +5492,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback { if (width <= 0 || height <= 0 || (width * height * (opaque ? 2 : 4) >= // Projected bitmap size in bytes ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) { - destroyDrawingCache(); + if (mDrawingCache != null) { + mDrawingCache.recycle(); + } + mDrawingCache = null; return; } boolean clear = true; - Bitmap bitmap = mDrawingCache == null ? null : mDrawingCache.get(); + Bitmap bitmap = mDrawingCache; if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) { @@ -5516,11 +5525,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } // Try to cleanup memory - if (bitmap != null) bitmap.recycle(); + if (mDrawingCache != null) { + mDrawingCache.recycle(); + } try { - bitmap = Bitmap.createBitmap(width, height, quality); - mDrawingCache = new SoftReference<Bitmap>(bitmap); + mDrawingCache = bitmap = Bitmap.createBitmap(width, height, quality); } catch (OutOfMemoryError e) { // If there is not enough memory to create the bitmap cache, just // ignore the issue as bitmap caches are not required to draw the @@ -8050,8 +8060,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { shader = new LinearGradient(0, 0, 0, 1, color, 0, Shader.TileMode.CLAMP); paint.setShader(shader); - // Restore the default transfer mode (src_over) - paint.setXfermode(null); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)); } } } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index d3f48c6..2f7b0d1 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -56,7 +56,7 @@ public class ViewConfiguration { /** * Defines the duration in milliseconds we will wait to see if a touch event - * is a tap or a scroll. If the user does not move within this interval, it is + * is a top or a scroll. If the user does not move within this interval, it is * considered to be a tap. */ private static final int TAP_TIMEOUT = 100; @@ -213,7 +213,7 @@ public class ViewConfiguration { } /** - * @return the length of the fading edges in pixels + * @return Defines the length of the fading edges in pixels * * @deprecated Use {@link #getScaledFadingEdgeLength()} instead. */ @@ -223,14 +223,14 @@ public class ViewConfiguration { } /** - * @return the length of the fading edges in pixels + * @return Defines the length of the fading edges in pixels */ public int getScaledFadingEdgeLength() { return mFadingEdgeLength; } /** - * @return the duration in milliseconds of the pressed state in child + * @return Defines the duration in milliseconds of the pressed state in child * components. */ public static int getPressedStateDuration() { @@ -238,7 +238,7 @@ public class ViewConfiguration { } /** - * @return the duration in milliseconds before a press turns into + * @return Defines the duration in milliseconds before a press turns into * a long press */ public static int getLongPressTimeout() { @@ -246,8 +246,8 @@ public class ViewConfiguration { } /** - * @return the duration in milliseconds we will wait to see if a touch event - * is a tap or a scroll. If the user does not move within this interval, it is + * @return Defines the duration in milliseconds we will wait to see if a touch event + * is a top or a scroll. If the user does not move within this interval, it is * considered to be a tap. */ public static int getTapTimeout() { @@ -255,7 +255,7 @@ public class ViewConfiguration { } /** - * @return the duration in milliseconds we will wait to see if a touch event + * @return Defines the duration in milliseconds we will wait to see if a touch event * is a jump tap. If the user does not move within this interval, it is * considered to be a tap. */ @@ -264,7 +264,7 @@ public class ViewConfiguration { } /** - * @return the duration in milliseconds between the first tap's up event and + * @return Defines the duration in milliseconds between the first tap's up event and * the second tap's down event for an interaction to be considered a * double-tap. * @hide pending API council diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index de64d0e..3cfaf1b 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -143,7 +143,6 @@ public final class ViewRoot extends Handler implements ViewParent, boolean mFullRedrawNeeded; boolean mNewSurfaceNeeded; boolean mHasHadWindowFocus; - boolean mLastWasImTarget; boolean mWindowAttributesChanged = false; @@ -999,21 +998,6 @@ public final class ViewRoot extends Handler implements ViewParent, mNewSurfaceNeeded = false; mViewVisibility = viewVisibility; - if (mAttachInfo.mHasWindowFocus) { - final boolean imTarget = WindowManager.LayoutParams - .mayUseInputMethod(mWindowAttributes.flags); - if (imTarget != mLastWasImTarget) { - mLastWasImTarget = imTarget; - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null && imTarget) { - imm.startGettingWindowFocus(mView); - imm.onWindowFocus(mView, mView.findFocus(), - mWindowAttributes.softInputMode, - !mHasHadWindowFocus, mWindowAttributes.flags); - } - } - } - boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw(); if (!cancelDraw && !newSurface) { @@ -1192,7 +1176,7 @@ public final class ViewRoot extends Handler implements ViewParent, // properly re-composite its drawing on a transparent // background. This automatically respects the clip/dirty region if (!canvas.isOpaque()) { - canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR); + canvas.drawColor(0xff0000ff, PorterDuff.Mode.CLEAR); } else if (yoff != 0) { // If we are applying an offset, we need to clear the area // where the offset doesn't appear to avoid having garbage @@ -1624,13 +1608,10 @@ public final class ViewRoot extends Handler implements ViewParent, } } - mLastWasImTarget = WindowManager.LayoutParams - .mayUseInputMethod(mWindowAttributes.flags); - InputMethodManager imm = InputMethodManager.peekInstance(); if (mView != null) { - if (hasWindowFocus && imm != null && mLastWasImTarget) { - imm.startGettingWindowFocus(mView); + if (hasWindowFocus && imm != null) { + imm.startGettingWindowFocus(); } mView.dispatchWindowFocusChanged(hasWindowFocus); } @@ -1638,7 +1619,7 @@ public final class ViewRoot extends Handler implements ViewParent, // Note: must be done after the focus change callbacks, // so all of the view state is set up correctly. if (hasWindowFocus) { - if (imm != null && mLastWasImTarget) { + if (imm != null) { imm.onWindowFocus(mView, mView.findFocus(), mWindowAttributes.softInputMode, !mHasHadWindowFocus, mWindowAttributes.flags); @@ -1995,9 +1976,6 @@ public final class ViewRoot extends Handler implements ViewParent, if (event.getAction() != KeyEvent.ACTION_DOWN) { return false; } - if ((event.getFlags()&KeyEvent.FLAG_KEEP_TOUCH_MODE) != 0) { - return false; - } // only relevant if we are in touch mode if (!mAttachInfo.mInTouchMode) { @@ -2117,7 +2095,8 @@ public final class ViewRoot extends Handler implements ViewParent, // If it is possible for this window to interact with the input // method window, then we want to first dispatch our key events // to the input method. - if (mLastWasImTarget) { + if (WindowManager.LayoutParams.mayUseInputMethod( + mWindowAttributes.flags)) { InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null && mView != null) { int seq = enqueuePendingEvent(event, sendDone); @@ -2147,10 +2126,6 @@ public final class ViewRoot extends Handler implements ViewParent, sWindowSession.finishKey(mWindow); } catch (RemoteException e) { } - } else { - Log.w("ViewRoot", "handleFinishedEvent(seq=" + seq - + " handled=" + handled + " ev=" + event - + ") neither delivering nor finishing key"); } } } @@ -2473,8 +2448,6 @@ public final class ViewRoot extends Handler implements ViewParent, final ViewRoot viewRoot = mViewRoot.get(); if (viewRoot != null) { viewRoot.dispatchKey(event); - } else { - Log.w("ViewRoot.W", "Key event " + event + " but no ViewRoot available!"); } } diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 52b4107..6fbc174 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -341,13 +341,6 @@ public class BaseInputConnection implements InputConnection { /** * The default implementation does nothing. */ - public boolean performEditorAction(int actionCode) { - return false; - } - - /** - * The default implementation does nothing. - */ public boolean performContextMenuAction(int id) { return false; } diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java index 0405371..b2f26d7 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -25,100 +25,17 @@ public class EditorInfo implements InputType, Parcelable { public int inputType = TYPE_NULL; /** - * Set of bits in {@link #imeOptions} that provide alternative actions - * associated with the "enter" key. This both helps the IME provide - * better feedback about what the enter key will do, and also allows it - * to provide alternative mechanisms for providing that command. - */ - public static final int IME_MASK_ACTION = 0x000000ff; - - /** - * Bits of {@link #IME_MASK_ACTION}: there is no special action - * associated with this editor. - */ - public static final int IME_ACTION_NONE = 0x00000000; - - /** - * Bits of {@link #IME_MASK_ACTION}: the action key performs a "go" - * operation to take the user to the target of the text they typed. - * Typically used, for example, when entering a URL. - */ - public static final int IME_ACTION_GO = 0x00000001; - - /** - * Bits of {@link #IME_MASK_ACTION}: the action key performs a "search" - * operation, taking the user to the results of searching for the text - * the have typed (in whatever context is appropriate). - */ - public static final int IME_ACTION_SEARCH = 0x00000002; - - /** - * Bits of {@link #IME_MASK_ACTION}: the action key performs a "send" - * operation, delivering the text to its target. This is typically used - * when composing a message. - */ - public static final int IME_ACTION_SEND = 0x00000003; - - /** - * Bits of {@link #IME_MASK_ACTION}: the action key performs a "next" - * operation, taking the user to the next field that will accept text. - */ - public static final int IME_ACTION_NEXT = 0x00000004; - - /** - * Flag of {@link #imeOptions}: used in conjunction with - * {@link #IME_MASK_ACTION}, this indicates that the action should not - * be available in-line as the same as a "enter" key. Typically this is - * because the action has such a significant impact or is not recoverable - * enough that accidentally hitting it should be avoided, such as sending - * a message. - */ - public static final int IME_FLAG_NO_ENTER_ACTION = 0x40000000; - - /** - * Generic non-special type for {@link #imeOptions}. - */ - public static final int IME_NORMAL = 0x00000000; - - /** - * Special code for when the ime option has been undefined. This is not - * used with the EditorInfo structure, but can be used elsewhere. - */ - public static final int IME_UNDEFINED = 0x80000000; - - /** - * Extended type information for the editor, to help the IME better - * integrate with it. - */ - public int imeOptions = IME_NORMAL; - - /** - * A string supplying additional information options that are - * private to a particular IME implementation. The string must be + * A string supplying additional information about the content type that + * is private to a particular IME implementation. The string must be * scoped to a package owned by the implementation, to ensure there are * no conflicts between implementations, but other than that you can put * whatever you want in it to communicate with the IME. For example, * you could have a string that supplies an argument like * <code>"com.example.myapp.SpecialMode=3"</code>. This field is can be - * filled in from the {@link android.R.attr#privateImeOptions} + * filled in from the {@link android.R.attr#editorPrivateContentType} * attribute of a TextView. */ - public String privateImeOptions = null; - - /** - * In some cases an IME may be able to display an arbitrary label for - * a command the user can perform, which you can specify here. You can - * not count on this being used. - */ - public CharSequence actionLabel = null; - - /** - * If {@link #actionLabel} has been given, this is the id for that command - * when the user presses its button that is delivered back with - * {@link InputConnection#performEditorAction(int) - * InputConnection.performEditorAction()}. - */ - public int actionId = 0; + public String privateContentType = null; /** * The text offset of the start of the selection at the time editing @@ -189,10 +106,7 @@ public class EditorInfo implements InputType, Parcelable { */ public void dump(Printer pw, String prefix) { pw.println(prefix + "inputType=0x" + Integer.toHexString(inputType) - + " imeOptions=0x" + Integer.toHexString(imeOptions) - + " privateImeOptions=" + privateImeOptions); - pw.println(prefix + "actionLabel=" + actionLabel - + " actionId=" + actionId); + + " privateContentType=" + privateContentType); pw.println(prefix + "initialSelStart=" + initialSelStart + " initialSelEnd=" + initialSelEnd + " initialCapsMode=0x" @@ -213,10 +127,7 @@ public class EditorInfo implements InputType, Parcelable { */ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(inputType); - dest.writeInt(imeOptions); - dest.writeString(privateImeOptions); - TextUtils.writeToParcel(actionLabel, dest, flags); - dest.writeInt(actionId); + dest.writeString(privateContentType); dest.writeInt(initialSelStart); dest.writeInt(initialSelEnd); dest.writeInt(initialCapsMode); @@ -235,10 +146,7 @@ public class EditorInfo implements InputType, Parcelable { public EditorInfo createFromParcel(Parcel source) { EditorInfo res = new EditorInfo(); res.inputType = source.readInt(); - res.imeOptions = source.readInt(); - res.privateImeOptions = source.readString(); - res.actionLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); - res.actionId = source.readInt(); + res.privateContentType = source.readString(); res.initialSelStart = source.readInt(); res.initialSelEnd = source.readInt(); res.initialCapsMode = source.readInt(); diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index 32cce35..530127d 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -208,24 +208,10 @@ public interface InputConnection { /** * Set the selection of the text editor. To set the cursor position, * start and end should have the same value. - * @return Returns true on success, false if the input connection is no longer - * valid. */ public boolean setSelection(int start, int end); /** - * Have the editor perform an action it has said it can do. - * - * @param editorAction This must be one of the action constants for - * {@link EditorInfo#imeOptions EditorInfo.editorType}, such as - * {@link EditorInfo#IME_ACTION_GO EditorInfo.EDITOR_ACTION_GO}. - * - * @return Returns true on success, false if the input connection is no longer - * valid. - */ - public boolean performEditorAction(int editorAction); - - /** * Perform a context menu action on the field. The given id may be one of: * {@link android.R.id#selectAll}, * {@link android.R.id#startSelectingText}, {@link android.R.id#stopSelectingText}, diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index e9e4703..91fa211 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -222,11 +222,6 @@ public final class InputMethodManager { // ----------------------------------------------------------- /** - * This is the root view of the overall window that currently has input - * method focus. - */ - View mCurRootView; - /** * This is the view that should currently be served by an input method, * regardless of the state of setting that up. */ @@ -845,13 +840,6 @@ public final class InputMethodManager { void focusInLocked(View view) { if (DEBUG) Log.v(TAG, "focusIn: " + view); - - if (mCurRootView != view.getRootView()) { - // This is a request from a window that isn't in the window with - // IME focus, so ignore it. - return; - } - // Okay we have a new view that is being served. if (mServedView != view) { mCurrentTextBoxAttribute = null; @@ -925,7 +913,7 @@ public final class InputMethodManager { } /** - * Called by ViewRoot when its window gets input focus. + * Called by ViewRoot the first time it gets window focus. * @hide */ public void onWindowFocus(View rootView, View focusedView, int softInputMode, @@ -958,10 +946,9 @@ public final class InputMethodManager { } /** @hide */ - public void startGettingWindowFocus(View rootView) { + public void startGettingWindowFocus() { synchronized (mH) { mWindowFocusedView = null; - mCurRootView = rootView; } } @@ -1178,7 +1165,6 @@ public final class InputMethodManager { + " mBindSequence=" + mBindSequence + " mCurId=" + mCurId); p.println(" mCurMethod=" + mCurMethod); - p.println(" mCurRootView=" + mCurRootView); p.println(" mServedView=" + mServedView); p.println(" mLastServedView=" + mLastServedView); p.println(" mServedConnecting=" + mServedConnecting); diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java index 84aeb83..4f8e5e4 100644 --- a/core/java/android/webkit/CallbackProxy.java +++ b/core/java/android/webkit/CallbackProxy.java @@ -907,12 +907,10 @@ class CallbackProxy extends Handler { } public void onReceivedIcon(Bitmap icon) { - // The current item might be null if the icon was already stored in the - // database and this is a new WebView. - WebHistoryItem i = mBackForwardList.getCurrentItem(); - if (i != null) { - i.setFavicon(icon); + if (Config.DEBUG && mBackForwardList.getCurrentItem() == null) { + throw new AssertionError(); } + mBackForwardList.getCurrentItem().setFavicon(icon); // Do an unsynchronized quick check to avoid posting if no callback has // been set. if (mWebChromeClient == null) { diff --git a/core/java/android/webkit/TextDialog.java b/core/java/android/webkit/TextDialog.java index 8a82411..c2620a5 100644 --- a/core/java/android/webkit/TextDialog.java +++ b/core/java/android/webkit/TextDialog.java @@ -25,6 +25,8 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.RectShape; +import android.os.Handler; +import android.os.Message; import android.text.Editable; import android.text.InputFilter; import android.text.Selection; @@ -41,6 +43,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewConfiguration; import android.widget.AbsoluteLayout.LayoutParams; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; @@ -79,6 +82,22 @@ import java.util.ArrayList; // FIXME: This can be replaced with TextView.NO_FILTERS if that // is made public/protected. private static final InputFilter[] NO_FILTERS = new InputFilter[0]; + // The time of the last enter down, so we know whether to perform a long + // press. + private long mDownTime; + + private boolean mTrackballDown = false; + private static int LONGPRESS = 1; + private Handler mHandler = new Handler() { + public void handleMessage(Message msg) { + if (msg.what == LONGPRESS) { + if (mTrackballDown) { + performLongClick(); + mTrackballDown = false; + } + } + } + }; /** * Create a new TextDialog. @@ -116,13 +135,6 @@ import java.util.ArrayList; } @Override - protected boolean shouldAdvanceFocusOnEnter() { - // In the browser, single line textfields use enter as a form submit, - // so we never want to advance the focus on enter. - return false; - } - - @Override public boolean dispatchKeyEvent(KeyEvent event) { if (event.isSystem()) { return super.dispatchKeyEvent(event); @@ -140,33 +152,43 @@ import java.util.ArrayList; return true; } - if ((mSingle && KeyEvent.KEYCODE_ENTER == keyCode)) { + // For single-line textfields, return key should not be handled + // here. Instead, the WebView is passed the key up, so it may fire a + // submit/onClick. + // Center key should always be passed to a potential onClick + if ((mSingle && KeyEvent.KEYCODE_ENTER == keyCode) + || KeyEvent.KEYCODE_DPAD_CENTER == keyCode) { if (isPopupShowing()) { - return super.dispatchKeyEvent(event); + super.dispatchKeyEvent(event); + return true; } - if (!down) { - // Hide the keyboard, since the user has just submitted this - // form. The submission happens thanks to the two calls - // to sendDomEvent. + if (down) { + if (event.getRepeatCount() == 0) { + mGotEnterDown = true; + mDownTime = event.getEventTime(); + // Send the keydown when the up comes, so that we have + // a chance to handle a long press. + } else if (mGotEnterDown && event.getEventTime() - mDownTime > + ViewConfiguration.getLongPressTimeout()) { + performLongClick(); + mGotEnterDown = false; + } + } else if (mGotEnterDown) { + mGotEnterDown = false; + if (KeyEvent.KEYCODE_DPAD_CENTER == keyCode) { + mWebView.shortPressOnTextField(); + return true; + } + // If we reached here, then this is a single line textfield, and + // the user pressed ENTER. In this case, we want to hide the + // soft input method. InputMethodManager.getInstance(mContext) .hideSoftInputFromWindow(getWindowToken(), 0); sendDomEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); sendDomEvent(event); } - return super.dispatchKeyEvent(event); - } else if (KeyEvent.KEYCODE_DPAD_CENTER == keyCode) { - // Note that this handles center key and trackball. - if (isPopupShowing()) { - return super.dispatchKeyEvent(event); - } - // Center key should be passed to a potential onClick - if (!down) { - mWebView.shortPressOnTextField(); - } - // Pass to super to handle longpress. - return super.dispatchKeyEvent(event); + return true; } - // Ensure there is a layout so arrow keys are handled properly. if (getLayout() == null) { measure(mWidthSpec, mHeightSpec); @@ -203,8 +225,9 @@ import java.util.ArrayList; case KeyEvent.KEYCODE_DPAD_DOWN: isArrowKey = true; break; + case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_ENTER: - // For multi-line text boxes, newlines will + // For multi-line text boxes, newlines and dpad center will // trigger onTextChanged for key down (which will send both // key up and key down) but not key up. mGotEnterDown = true; @@ -246,7 +269,7 @@ import java.util.ArrayList; // with WebCore's notion of the current selection, reset the selection // to what it was before the key event. Selection.setSelection(text, oldStart, oldEnd); - // Ignore the key up event for newlines. This prevents + // Ignore the key up event for newlines or dpad center. This prevents // multiple newlines in the native textarea. if (mGotEnterDown && !down) { return true; @@ -368,8 +391,27 @@ import java.util.ArrayList; if (isPopupShowing()) { return super.onTrackballEvent(event); } - if (event.getAction() != MotionEvent.ACTION_MOVE) { - return false; + int action = event.getAction(); + switch (action) { + case MotionEvent.ACTION_DOWN: + if (!mTrackballDown) { + mTrackballDown = true; + mHandler.sendEmptyMessageDelayed(LONGPRESS, + ViewConfiguration.getLongPressTimeout()); + } + return true; + case MotionEvent.ACTION_UP: + if (mTrackballDown) { + mWebView.shortPressOnTextField(); + mTrackballDown = false; + mHandler.removeMessages(LONGPRESS); + } + return true; + case MotionEvent.ACTION_CANCEL: + mTrackballDown = false; + return true; + case MotionEvent.ACTION_MOVE: + // fall through } Spannable text = (Spannable) getText(); MovementMethod move = getMovementMethod(); @@ -400,6 +442,7 @@ import java.util.ArrayList; // hide the soft keyboard when the edit text is out of focus InputMethodManager.getInstance(mContext).hideSoftInputFromWindow( getWindowToken(), 0); + mHandler.removeMessages(LONGPRESS); mWebView.removeView(this); mWebView.requestFocus(); mScrollToAccommodateCursor = false; diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 5126ef0..417b657 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -71,7 +71,6 @@ import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.Scroller; import android.widget.Toast; -import android.widget.ZoomButtonsController; import android.widget.ZoomControls; import android.widget.ZoomRingController; import android.widget.FrameLayout; @@ -285,10 +284,8 @@ public class WebView extends AbsoluteLayout /** * Customizable constant */ - // pre-computed square of ViewConfiguration.getScaledTouchSlop() + // pre-computed square of ViewConfiguration.getTouchSlop() private int mTouchSlopSquare; - // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop() - private int mDoubleTapSlopSquare; // This should be ViewConfiguration.getTapTimeout() // But system time out is 100ms, which is too short for the browser. // In the browser, if it switches out of tap too soon, jump tap won't work. @@ -324,8 +321,6 @@ public class WebView extends AbsoluteLayout private int mContentHeight; // cache of value from WebViewCore static int MAX_FLOAT_CONTENT_WIDTH = 480; - // the calculated minimum content width for calculating the minimum scale. - // If it is 0, it means don't use it. private int mMinContentWidth; // Need to have the separate control for horizontal and vertical scrollbar @@ -558,9 +553,7 @@ public class WebView extends AbsoluteLayout return mExtra; } } - - private ZoomButtonsController mZoomButtonsController; - + private ZoomRingController mZoomRingController; private ImageView mZoomRingOverview; private Animation mZoomRingOverviewExitAnimation; @@ -624,9 +617,6 @@ public class WebView extends AbsoluteLayout / ZOOM_RING_STEPS; } mZoomRingController.setThumbAngle(angle * MAX_ZOOM_RING_ANGLE); - - // Don't show a thumb if the user cannot zoom - mZoomRingController.setThumbVisible(mMinZoomScale != mMaxZoomScale); // Show the zoom overview tab on the ring setZoomOverviewVisible(true); @@ -743,26 +733,6 @@ public class WebView extends AbsoluteLayout mZoomRingController.setPannerAcceleration(160); mZoomRingController.setPannerStartAcceleratingDuration(700); createZoomRingOverviewTab(); - mZoomButtonsController = new ZoomButtonsController(context, this); - mZoomButtonsController.setOverviewVisible(true); - mZoomButtonsController.setCallback(new ZoomButtonsController.OnZoomListener() { - public void onCenter(int x, int y) { - mZoomListener.onCenter(x, y); - } - - public void onOverview() { - mZoomButtonsController.setVisible(false); - zoomScrollOut(); - } - - public void onVisibilityChanged(boolean visible) { - mZoomListener.onVisibilityChanged(visible); - } - - public void onZoom(boolean zoomIn) { - mZoomListener.onSimpleZoom(zoomIn); - } - }); } private void init() { @@ -775,9 +745,6 @@ public class WebView extends AbsoluteLayout final int slop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); mTouchSlopSquare = slop * slop; mMinLockSnapReverseDistance = slop; - final int doubleTapslop = ViewConfiguration.get(getContext()) - .getScaledDoubleTapSlop(); - mDoubleTapSlopSquare = doubleTapslop * doubleTapslop; } private void createZoomRingOverviewTab() { @@ -796,7 +763,7 @@ public class WebView extends AbsoluteLayout FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER); // TODO: magic constant that's based on the zoom ring radius + some offset - lp.topMargin = 200; + lp.topMargin = 208; mZoomRingOverview.setLayoutParams(lp); mZoomRingOverview.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { @@ -2338,19 +2305,8 @@ public class WebView extends AbsoluteLayout /** * Use this function to bind an object to Javascript so that the * methods can be accessed from Javascript. - * <p><strong>IMPORTANT:</strong> - * <ul> - * <li> Using addJavascriptInterface() allows JavaScript to control your - * application. This can be a very useful feature or a dangerous security - * issue. When the HTML in the WebView is untrustworthy (for example, part - * or all of the HTML is provided by some person or process), then an - * attacker could inject HTML that will execute your code and possibly any - * code of the attacker's choosing.<br> - * Do not use addJavascriptInterface() unless all of the HTML in this - * WebView was written by you.</li> - * <li> The Java object that is bound runs in another thread and not in - * the thread that it was constructed in.</li> - * </ul></p> + * IMPORTANT, the object that is bound runs in another thread and + * not in the thread that it was constructed in. * @param obj The class instance to bind to Javascript * @param interfaceName The name to used to expose the class in Javascript */ @@ -3013,8 +2969,8 @@ public class WebView extends AbsoluteLayout if (lp != null) { // Take the last touch and adjust for the location of the // TextDialog. - float x = mLastTouchX + (float) (mScrollX - lp.x); - float y = mLastTouchY + (float) (mScrollY - lp.y); + float x = mLastTouchX - lp.x; + float y = mLastTouchY - lp.y; mTextEntry.fakeTouchEvent(x, y); } } @@ -3208,9 +3164,6 @@ public class WebView extends AbsoluteLayout mSelectX = mScrollX + (int) mLastTouchX; mSelectY = mScrollY + (int) mLastTouchY; } - int contentX = viewToContent((int) mLastTouchX + mScrollX); - int contentY = viewToContent((int) mLastTouchY + mScrollY); - nativeClearFocus(contentX, contentY); } if (keyCode >= KeyEvent.KEYCODE_DPAD_UP @@ -3402,9 +3355,6 @@ public class WebView extends AbsoluteLayout public void emulateShiftHeld() { mExtendSelection = false; mShiftIsPressed = true; - int contentX = viewToContent((int) mLastTouchX + mScrollX); - int contentY = viewToContent((int) mLastTouchY + mScrollY); - nativeClearFocus(contentX, contentY); } private boolean commitCopy() { @@ -3451,7 +3401,6 @@ public class WebView extends AbsoluteLayout // Clean up the zoom ring mZoomRingController.setVisible(false); - mZoomButtonsController.setVisible(false); } // Implementation for OnHierarchyChangeListener @@ -3500,17 +3449,8 @@ public class WebView extends AbsoluteLayout // false for the first parameter } } else { - if (!mZoomButtonsController.isVisible()) { - /* - * The zoom controls come in their own window, so our window - * loses focus. Our policy is to not draw the focus ring if - * our window is not focused, but this is an exception since - * the user can still navigate the web page with the zoom - * controls showing. - */ - // If our window has lost focus, stop drawing the focus ring - mDrawFocusRing = false; - } + // If our window has lost focus, stop drawing the focus ring + mDrawFocusRing = false; mGotKeyDown = false; mShiftIsPressed = false; if (mNativeClass != 0) { @@ -3652,8 +3592,7 @@ public class WebView extends AbsoluteLayout + mTouchMode); } - if ((mZoomRingController.isVisible() || mZoomButtonsController.isVisible()) - && mInZoomTapDragMode) { + if (mZoomRingController.isVisible() && mInZoomTapDragMode) { if (ev.getAction() == MotionEvent.ACTION_UP) { // Just released the second tap, no longer in tap-drag mode mInZoomTapDragMode = false; @@ -3691,9 +3630,6 @@ public class WebView extends AbsoluteLayout mLastSentTouchTime = eventTime; } - int deltaX = (int) (mLastTouchX - x); - int deltaY = (int) (mLastTouchY - y); - switch (action) { case MotionEvent.ACTION_DOWN: { if (mTouchMode == SCROLL_ZOOM_ANIMATION_IN @@ -3719,23 +3655,16 @@ public class WebView extends AbsoluteLayout , viewToContent(mSelectY), false); mTouchSelection = mExtendSelection = true; } else if (!ZoomRingController.useOldZoom(mContext) && - mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP) && - (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare)) { + mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) { // Found doubletap, invoke the zoom controller mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP); - int contentX = viewToContent((int) mLastTouchX + mScrollX); - int contentY = viewToContent((int) mLastTouchY + mScrollY); - if (inEditingMode()) { - mTextEntry.updateCachedTextfield(); - } - nativeClearFocus(contentX, contentY); + mZoomRingController.setVisible(true); mInZoomTapDragMode = true; if (mLogEvent) { EventLog.writeEvent(EVENT_LOG_DOUBLE_TAP_DURATION, (eventTime - mLastTouchUpTime), eventTime); } - return mZoomRingController.handleDoubleTapEvent(ev) || - mZoomButtonsController.handleDoubleTapEvent(ev); + return mZoomRingController.handleDoubleTapEvent(ev); } else { mTouchMode = TOUCH_INIT_MODE; mPreventDrag = mForwardTouchEvents; @@ -3772,6 +3701,9 @@ public class WebView extends AbsoluteLayout } mVelocityTracker.addMovement(ev); + int deltaX = (int) (mLastTouchX - x); + int deltaY = (int) (mLastTouchY - y); + if (mTouchMode != TOUCH_DRAG_MODE) { if (mTouchMode == TOUCH_SELECT_MODE) { mSelectX = mScrollX + (int) x; diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 6ab088d..a7261c5 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -1294,9 +1294,7 @@ final class WebViewCore { draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight); if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID"); Message.obtain(mWebView.mPrivateHandler, - WebView.NEW_PICTURE_MSG_ID, - mViewportMinimumScale == 0 ? nativeGetContentMinPrefWidth() - : 0, + WebView.NEW_PICTURE_MSG_ID, nativeGetContentMinPrefWidth(), 0, draw).sendToTarget(); nativeCheckNavCache(); if (mWebkitScrollX != 0 || mWebkitScrollY != 0) { diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 9da78d0..f362e22 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -17,6 +17,7 @@ package android.widget; import android.content.Context; +import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; @@ -27,7 +28,6 @@ import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; import android.text.Editable; -import android.text.TextUtils; import android.text.TextWatcher; import android.util.AttributeSet; import android.view.Gravity; @@ -893,16 +893,25 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mSyncMode = SYNC_FIRST_POSITION; } - setFilterText(ss.filter); + // Don't restore the type filter window when there is no keyboard + if (acceptFilter()) { + String filterText = ss.filter; + setFilterText(filterText); + } requestLayout(); } private boolean acceptFilter() { final Context context = mContext; + final Configuration configuration = context.getResources().getConfiguration(); + final boolean keyboardShowing = configuration.keyboardHidden != + Configuration.KEYBOARDHIDDEN_YES; + final boolean hasKeyboard = configuration.keyboard != Configuration.KEYBOARD_NOKEYS; final InputMethodManager inputManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); - return !inputManager.isFullscreenMode(); + return (hasKeyboard && keyboardShowing) || + (!hasKeyboard && !inputManager.isFullscreenMode()); } /** @@ -913,7 +922,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te */ public void setFilterText(String filterText) { // TODO: Should we check for acceptFilter()? - if (mTextFilterEnabled && !TextUtils.isEmpty(filterText)) { + if (mTextFilterEnabled && filterText != null && filterText.length() > 0) { createTextFilter(false); // This is going to call our listener onTextChanged, but we might not // be ready to bring up a window yet @@ -933,18 +942,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } - /** - * Returns the list's text filter, if available. - * @return the list's text filter or null if filtering isn't enabled - * @hide pending API Council approval - */ - public CharSequence getTextFilter() { - if (mTextFilterEnabled && mTextFilter != null) { - return mTextFilter.getText(); - } - return null; - } - @Override protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index 1d553f1..b046a6b 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -40,15 +40,9 @@ public abstract class AbsSeekBar extends ProgressBar { * Whether this is user seekable. */ boolean mIsUserSeekable = true; - - /** - * On key presses (right or left), the amount to increment/decrement the - * progress. - */ - private int mKeyProgressIncrement = 1; private static final int NO_ALPHA = 0xFF; - private float mDisabledAlpha; + float mDisabledAlpha; public AbsSeekBar(Context context) { super(context); @@ -107,39 +101,6 @@ public abstract class AbsSeekBar extends ProgressBar { invalidate(); } - /** - * Sets the amount of progress changed via the arrow keys. - * - * @param increment The amount to increment or decrement when the user - * presses the arrow keys. - */ - public void setKeyProgressIncrement(int increment) { - mKeyProgressIncrement = increment < 0 ? -increment : increment; - } - - /** - * Returns the amount of progress changed via the arrow keys. - * <p> - * By default, this will be a value that is derived from the max progress. - * - * @return The amount to increment or decrement when the user presses the - * arrow keys. This will be positive. - */ - public int getKeyProgressIncrement() { - return mKeyProgressIncrement; - } - - @Override - public synchronized void setMax(int max) { - super.setMax(max); - - if ((mKeyProgressIncrement == 0) || (getMax() / mKeyProgressIncrement > 20)) { - // It will take the user too long to change this via keys, change it - // to something more reasonable - setKeyProgressIncrement(Math.max(1, Math.round((float) getMax() / 20))); - } - } - @Override protected boolean verifyDrawable(Drawable who) { return who == mThumb || super.verifyDrawable(who); @@ -360,12 +321,12 @@ public abstract class AbsSeekBar extends ProgressBar { switch (keyCode) { case KeyEvent.KEYCODE_DPAD_LEFT: if (progress <= 0) break; - setProgress(progress - mKeyProgressIncrement, true); + setProgress(progress - 1, true); return true; case KeyEvent.KEYCODE_DPAD_RIGHT: if (progress >= getMax()) break; - setProgress(progress + mKeyProgressIncrement, true); + setProgress(progress + 1, true); return true; } diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java index f847bc3..cf9c588 100644 --- a/core/java/android/widget/AnalogClock.java +++ b/core/java/android/widget/AnalogClock.java @@ -48,6 +48,7 @@ public class AnalogClock extends View { private int mDialHeight; private boolean mAttached; + private long mLastTime; private final Handler mHandler = new Handler(); private float mMinutes; @@ -95,6 +96,7 @@ public class AnalogClock extends View { protected void onAttachedToWindow() { super.onAttachedToWindow(); + onTimeChanged(); if (!mAttached) { mAttached = true; IntentFilter filter = new IntentFilter(); @@ -105,15 +107,6 @@ public class AnalogClock extends View { getContext().registerReceiver(mIntentReceiver, filter, null, mHandler); } - - // NOTE: It's safe to do these after registering the receiver since the receiver always runs - // in the main thread, therefore the receiver can't run before this method returns. - - // The time zone may have changed while the receiver wasn't registered, so update the Time - mCalendar = new Time(); - - // Make sure we update to the current time - onTimeChanged(); } @Override @@ -219,7 +212,9 @@ public class AnalogClock extends View { } private void onTimeChanged() { - mCalendar.setToNow(); + long time = System.currentTimeMillis(); + mCalendar.set(time); + mLastTime = time; int hour = mCalendar.hour; int minute = mCalendar.minute; @@ -236,6 +231,8 @@ public class AnalogClock extends View { if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) { String tz = intent.getStringExtra("time-zone"); mCalendar = new Time(TimeZone.getTimeZone(tz).getID()); + } else { + mCalendar = new Time(); } onTimeChanged(); diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index 0c1c72a..7a51676 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -78,8 +78,6 @@ import com.android.internal.R; * @attr ref android.R.styleable#AutoCompleteTextView_completionThreshold * @attr ref android.R.styleable#AutoCompleteTextView_completionHintView * @attr ref android.R.styleable#AutoCompleteTextView_dropDownSelector - * @attr ref android.R.styleable#AutoCompleteTextView_dropDownAnchor - * @attr ref android.R.styleable#AutoCompleteTextView_dropDownWidth */ public class AutoCompleteTextView extends EditText implements Filter.FilterListener { static final boolean DEBUG = false; @@ -98,9 +96,6 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe private DropDownListView mDropDownList; private int mDropDownVerticalOffset; private int mDropDownHorizontalOffset; - private int mDropDownAnchorId; - private View mDropDownAnchorView; // view is retrieved lazily from id once needed - private int mDropDownWidth; private Drawable mDropDownListHighlight; @@ -152,18 +147,6 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe a.getDimension(R.styleable.AutoCompleteTextView_dropDownVerticalOffset, 0.0f); mDropDownHorizontalOffset = (int) a.getDimension(R.styleable.AutoCompleteTextView_dropDownHorizontalOffset, 0.0f); - - // Get the anchor's id now, but the view won't be ready, so wait to actually get the - // view and store it in mDropDownAnchorView lazily in getDropDownAnchorView later. - // Defaults to NO_ID, in which case the getDropDownAnchorView method will simply return - // this TextView, as a default anchoring point. - mDropDownAnchorId = a.getResourceId(R.styleable.AutoCompleteTextView_dropDownAnchor, - View.NO_ID); - - // For dropdown width, the developer can specify a specific width, or FILL_PARENT - // (for full screen width) or WRAP_CONTENT (to match the width of the anchored view). - mDropDownWidth = a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownWidth, - ViewGroup.LayoutParams.WRAP_CONTENT); mHintResource = a.getResourceId(R.styleable.AutoCompleteTextView_completionHintView, R.layout.simple_dropdown_hint); @@ -204,49 +187,6 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe public void setCompletionHint(CharSequence hint) { mHintText = hint; } - - /** - * <p>Returns the current width for the auto-complete drop down list. This can - * be a fixed width, or {@link ViewGroup.LayoutParams#FILL_PARENT} to fill the screen, or - * {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the width of its anchor view.</p> - * - * @return the width for the drop down list - */ - public int getDropDownWidth() { - return mDropDownWidth; - } - - /** - * <p>Sets the current width for the auto-complete drop down list. This can - * be a fixed width, or {@link ViewGroup.LayoutParams#FILL_PARENT} to fill the screen, or - * {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the width of its anchor view.</p> - * - * @param width the width to use - */ - public void setDropDownWidth(int width) { - mDropDownWidth = width; - } - - /** - * <p>Returns the id for the view that the auto-complete drop down list is anchored to.</p> - * - * @return the view's id, or {@link View#NO_ID} if none specified - */ - public int getDropDownAnchor() { - return mDropDownAnchorId; - } - - /** - * <p>Sets the view to which the auto-complete drop down list should anchor. The view - * corresponding to this id will not be loaded until the next time it is needed to avoid - * loading a view which is not yet instantiated.</p> - * - * @param id the id to anchor the drop down list view to - */ - public void setDropDownAnchor(int id) { - mDropDownAnchorId = id; - mDropDownAnchorView = null; - } /** * <p>Returns the number of characters the user must type before the drop @@ -801,18 +741,6 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe return result; } - - /** - * <p>Used for lazy instantiation of the anchor view from the id we have. If the value of - * the id is NO_ID or we can't find a view for the given id, we return this TextView as - * the default anchoring point.</p> - */ - private View getDropDownAnchorView() { - if (mDropDownAnchorView == null && mDropDownAnchorId != View.NO_ID) { - mDropDownAnchorView = getRootView().findViewById(mDropDownAnchorId); - } - return mDropDownAnchorView == null ? this : mDropDownAnchorView; - } /** * <p>Displays the drop down on screen.</p> @@ -820,37 +748,16 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe public void showDropDown() { int height = buildDropDown(); if (mPopup.isShowing()) { - int widthSpec; - if (mDropDownWidth == ViewGroup.LayoutParams.FILL_PARENT) { - // The call to PopupWindow's update method below can accept -1 for any - // value you do not want to update. - widthSpec = -1; - } else if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) { - widthSpec = getDropDownAnchorView().getWidth(); - } else { - widthSpec = mDropDownWidth; - } - mPopup.update(getDropDownAnchorView(), mDropDownHorizontalOffset, - mDropDownVerticalOffset, widthSpec, height); + mPopup.update(this, mDropDownHorizontalOffset, mDropDownVerticalOffset, + getWidth(), height); } else { - int widthSpec; - if (mDropDownWidth == ViewGroup.LayoutParams.FILL_PARENT) { - mPopup.setWindowLayoutMode(ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT); - } else { - mPopup.setWindowLayoutMode(0, ViewGroup.LayoutParams.WRAP_CONTENT); - if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) { - mPopup.setWidth(getDropDownAnchorView().getWidth()); - } else { - mPopup.setWidth(mDropDownWidth); - } - } + mPopup.setWindowLayoutMode(0, ViewGroup.LayoutParams.WRAP_CONTENT); + mPopup.setWidth(getWidth()); mPopup.setHeight(height); mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); mPopup.setOutsideTouchable(true); mPopup.setTouchInterceptor(new PopupTouchIntercepter()); - mPopup.showAsDropDown(getDropDownAnchorView(), - mDropDownHorizontalOffset, mDropDownVerticalOffset); + mPopup.showAsDropDown(this, mDropDownHorizontalOffset, mDropDownVerticalOffset); mDropDownList.setSelection(ListView.INVALID_POSITION); mDropDownList.hideSelector(); mDropDownList.requestFocus(); diff --git a/core/java/android/widget/BaseAdapter.java b/core/java/android/widget/BaseAdapter.java index 532fd76..1921d73 100644 --- a/core/java/android/widget/BaseAdapter.java +++ b/core/java/android/widget/BaseAdapter.java @@ -42,10 +42,6 @@ public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter { mDataSetObservable.unregisterObserver(observer); } - /** - * Notifies the attached View that the underlying data has been changed - * and it should refresh itself. - */ public void notifyDataSetChanged() { mDataSetObservable.notifyChanged(); } diff --git a/core/java/android/widget/Chronometer.java b/core/java/android/widget/Chronometer.java index 91add58..369221e 100644 --- a/core/java/android/widget/Chronometer.java +++ b/core/java/android/widget/Chronometer.java @@ -69,10 +69,7 @@ public class Chronometer extends TextView { private Object[] mFormatterArgs = new Object[1]; private StringBuilder mFormatBuilder; private OnChronometerTickListener mOnChronometerTickListener; - private StringBuilder mRecycle = new StringBuilder(8); - - private static final int TICK_WHAT = 2; - + /** * Initialize this Chronometer object. * Sets the base to the current time. @@ -118,7 +115,6 @@ public class Chronometer extends TextView { @android.view.RemotableViewMethod public void setBase(long base) { mBase = base; - dispatchChronometerTick(); updateText(SystemClock.elapsedRealtime()); } @@ -220,10 +216,10 @@ public class Chronometer extends TextView { updateRunning(); } - private synchronized void updateText(long now) { + private void updateText(long now) { long seconds = now - mBase; seconds /= 1000; - String text = DateUtils.formatElapsedTime(mRecycle, seconds); + String text = DateUtils.formatElapsedTime(seconds); if (mFormat != null) { Locale loc = Locale.getDefault(); @@ -251,10 +247,7 @@ public class Chronometer extends TextView { if (running != mRunning) { if (running) { updateText(SystemClock.elapsedRealtime()); - dispatchChronometerTick(); - mHandler.sendMessageDelayed(Message.obtain(mHandler, TICK_WHAT), 1000); - } else { - mHandler.removeMessages(TICK_WHAT); + mHandler.sendMessageDelayed(Message.obtain(), 1000); } mRunning = running; } @@ -262,10 +255,10 @@ public class Chronometer extends TextView { private Handler mHandler = new Handler() { public void handleMessage(Message m) { - if (mRunning) { + if (mStarted) { updateText(SystemClock.elapsedRealtime()); dispatchChronometerTick(); - sendMessageDelayed(Message.obtain(this, TICK_WHAT), 1000); + sendMessageDelayed(Message.obtain(), 1000); } } }; diff --git a/core/java/android/widget/CursorAdapter.java b/core/java/android/widget/CursorAdapter.java index 898e501..3d758e7 100644 --- a/core/java/android/widget/CursorAdapter.java +++ b/core/java/android/widget/CursorAdapter.java @@ -348,21 +348,6 @@ public abstract class CursorAdapter extends BaseAdapter implements Filterable, mFilterQueryProvider = filterQueryProvider; } - /** - * Called when the {@link ContentObserver} on the cursor receives a change notification. - * The default implementation provides the auto-requery logic, but may be overridden by - * sub classes. - * - * @see ContentObserver#onChange(boolean) - * @hide pending API Council approval - */ - protected void onContentChanged() { - if (mAutoRequery && mCursor != null && !mCursor.isClosed()) { - if (Config.LOGV) Log.v("Cursor", "Auto requerying " + mCursor + " due to update"); - mDataValid = mCursor.requery(); - } - } - private class ChangeObserver extends ContentObserver { public ChangeObserver() { super(new Handler()); @@ -375,7 +360,10 @@ public abstract class CursorAdapter extends BaseAdapter implements Filterable, @Override public void onChange(boolean selfChange) { - onContentChanged(); + if (mAutoRequery && mCursor != null && !mCursor.isClosed()) { + if (Config.LOGV) Log.v("Cursor", "Auto requerying " + mCursor + " due to update"); + mDataValid = mCursor.requery(); + } } } diff --git a/core/java/android/widget/Filter.java b/core/java/android/widget/Filter.java index 1d0fd5e..a2316cf 100644 --- a/core/java/android/widget/Filter.java +++ b/core/java/android/widget/Filter.java @@ -45,6 +45,8 @@ public abstract class Filter { private Handler mThreadHandler; private Handler mResultHandler; + private String mConstraint; + private boolean mConstraintIsValid = false; /** * <p>Creates a new asynchronous filter.</p> @@ -82,6 +84,13 @@ public abstract class Filter { */ public final void filter(CharSequence constraint, FilterListener listener) { synchronized (this) { + String constraintAsString = constraint != null ? constraint.toString() : null; + if (mConstraintIsValid && ( + (constraintAsString == null && mConstraint == null) || + (constraintAsString != null && constraintAsString.equals(mConstraint)))) { + // nothing to do + return; + } if (mThreadHandler == null) { HandlerThread thread = new HandlerThread(THREAD_NAME); @@ -94,13 +103,16 @@ public abstract class Filter { RequestArguments args = new RequestArguments(); // make sure we use an immutable copy of the constraint, so that // it doesn't change while the filter operation is in progress - args.constraint = constraint != null ? constraint.toString() : null; + args.constraint = constraintAsString; args.listener = listener; message.obj = args; mThreadHandler.removeMessages(FILTER_TOKEN); mThreadHandler.removeMessages(FINISH_TOKEN); mThreadHandler.sendMessage(message); + + mConstraint = constraintAsString; + mConstraintIsValid = true; } } diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index 6bbf062..38bfc7c 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -924,23 +924,32 @@ public class GridView extends AbsListView { final int count = mItemCount; if (count > 0) { final View child = obtainView(0); + final int childViewType = mAdapter.getItemViewType(0); - AbsListView.LayoutParams p = (AbsListView.LayoutParams)child.getLayoutParams(); - if (p == null) { - p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, + AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams(); + if (lp == null) { + lp = new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0); + child.setLayoutParams(lp); } - p.viewType = mAdapter.getItemViewType(0); + lp.viewType = childViewType; - int childHeightSpec = getChildMeasureSpec( - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height); - int childWidthSpec = getChildMeasureSpec( - MeasureSpec.makeMeasureSpec(mColumnWidth, MeasureSpec.EXACTLY), 0, p.width); - child.measure(childWidthSpec, childHeightSpec); + final int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec, + mListPadding.left + mListPadding.right, lp.width); + + int lpHeight = lp.height; + int childHeightSpec; + if (lpHeight > 0) { + childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); + } else { + childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + } + + child.measure(childWidthSpec, childHeightSpec); childHeight = child.getMeasuredHeight(); - if (mRecycler.shouldRecycleViewType(p.viewType)) { + if (mRecycler.shouldRecycleViewType(childViewType)) { mRecycler.addScrapView(child); } } diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 652e30c..96fe595 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -875,7 +875,7 @@ public class HorizontalScrollView extends FrameLayout { int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); - final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, + final int childHeightMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height); final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index a4523b9..94d1bd1 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -840,7 +840,7 @@ public class ImageView extends View { @Override public int getBaseline() { - return mBaselineAligned ? getMeasuredHeight() : -1; + return mBaselineAligned ? getHeight() : -1; } /** diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 6df72d4..4e5989c 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -1011,13 +1011,34 @@ public class ListView extends AbsListView { if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED)) { final View child = obtainView(0); + final int childViewType = mAdapter.getItemViewType(0); - measureScrapChild(child, 0, widthMeasureSpec); + AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams(); + if (lp == null) { + lp = new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT, 0); + child.setLayoutParams(lp); + } + lp.viewType = childViewType; + + final int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec, + mListPadding.left + mListPadding.right, lp.width); + + int lpHeight = lp.height; + + int childHeightSpec; + if (lpHeight > 0) { + childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); + } else { + childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + } + + child.measure(childWidthSpec, childHeightSpec); childWidth = child.getMeasuredWidth(); childHeight = child.getMeasuredHeight(); - if (recycleOnMeasure()) { + if (mRecycler.shouldRecycleViewType(childViewType)) { mRecycler.addScrapView(child); } } @@ -1034,40 +1055,13 @@ public class ListView extends AbsListView { if (heightMode == MeasureSpec.AT_MOST) { // TODO: after first layout we should maybe start at the first visible position, not 0 - heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1); + heightSize = measureHeightOfChildren( + MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), + 0, NO_POSITION, heightSize, -1); } setMeasuredDimension(widthSize, heightSize); - mWidthMeasureSpec = widthMeasureSpec; - } - - private void measureScrapChild(View child, int position, int widthMeasureSpec) { - LayoutParams p = (LayoutParams) child.getLayoutParams(); - if (p == null) { - p = new LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT, 0); - } - p.viewType = mAdapter.getItemViewType(position); - - int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec, - mListPadding.left + mListPadding.right, p.width); - int lpHeight = p.height; - int childHeightSpec; - if (lpHeight > 0) { - childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); - } else { - childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - } - child.measure(childWidthSpec, childHeightSpec); - } - - /** - * @return True to recycle the views used to measure this ListView in - * UNSPECIFIED/AT_MOST modes, false otherwise. - * @hide - */ - protected boolean recycleOnMeasure() { - return true; + mWidthMeasureSpec = widthMeasureSpec; } /** @@ -1096,8 +1090,8 @@ public class ListView extends AbsListView { * startPosition is 0). * @return The height of this ListView with the given children. */ - final int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition, - final int maxHeight, int disallowPartialChildPosition) { + final int measureHeightOfChildren(final int widthMeasureSpec, final int startPosition, + int endPosition, final int maxHeight, int disallowPartialChildPosition) { final ListAdapter adapter = mAdapter; if (adapter == null) { @@ -1116,20 +1110,29 @@ public class ListView extends AbsListView { // mItemCount - 1 since endPosition parameter is inclusive endPosition = (endPosition == NO_POSITION) ? adapter.getCount() - 1 : endPosition; final AbsListView.RecycleBin recycleBin = mRecycler; - final boolean recyle = recycleOnMeasure(); - for (i = startPosition; i <= endPosition; ++i) { child = obtainView(i); + final int childViewType = adapter.getItemViewType(i); - measureScrapChild(child, i, widthMeasureSpec); + AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams(); + if (lp == null) { + lp = new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT, 0); + child.setLayoutParams(lp); + } + lp.viewType = childViewType; if (i > 0) { // Count the divider for all but one child returnedHeight += dividerHeight; } + child.measure(widthMeasureSpec, lp.height >= 0 + ? MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY) + : MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + // Recycle the view before we possibly return from the method - if (recyle) { + if (recycleBin.shouldRecycleViewType(childViewType)) { recycleBin.addScrapView(child); } @@ -1653,7 +1656,7 @@ public class ListView extends AbsListView { // Respect layout params that are already in the view. Otherwise make some up... // noinspection unchecked - AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams(); + AbsListView.LayoutParams p = (AbsListView.LayoutParams)child.getLayoutParams(); if (p == null) { p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0); @@ -1672,7 +1675,7 @@ public class ListView extends AbsListView { if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) { if (child instanceof Checkable) { - ((Checkable) child).setChecked(mCheckStates.get(position)); + ((Checkable)child).setChecked(mCheckStates.get(position)); } } diff --git a/core/java/android/widget/MultiAutoCompleteTextView.java b/core/java/android/widget/MultiAutoCompleteTextView.java index 05abc26..59a9310 100644 --- a/core/java/android/widget/MultiAutoCompleteTextView.java +++ b/core/java/android/widget/MultiAutoCompleteTextView.java @@ -126,7 +126,7 @@ public class MultiAutoCompleteTextView extends AutoCompleteTextView { Editable text = getText(); int end = getSelectionEnd(); - if (end < 0 || mTokenizer == null) { + if (end < 0) { return false; } @@ -147,7 +147,7 @@ public class MultiAutoCompleteTextView extends AutoCompleteTextView { public void performValidation() { Validator v = getValidator(); - if (v == null || mTokenizer == null) { + if (v == null) { return; } diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 53db77e..4a5cea1 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -30,7 +30,6 @@ import android.view.View.OnTouchListener; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.graphics.drawable.StateListDrawable; import android.os.IBinder; import android.content.Context; import android.content.res.TypedArray; @@ -103,8 +102,6 @@ public class PopupWindow { private Rect mTempRect = new Rect(); private Drawable mBackground; - private Drawable mAboveAnchorBackgroundDrawable; - private Drawable mBelowAnchorBackgroundDrawable; private boolean mAboveAnchor; @@ -167,43 +164,6 @@ public class PopupWindow { mBackground = a.getDrawable(R.styleable.PopupWindow_popupBackground); - // If this is a StateListDrawable, try to find and store the drawable to be - // used when the drop-down is placed above its anchor view, and the one to be - // used when the drop-down is placed below its anchor view. We extract - // the drawables ourselves to work around a problem with using refreshDrawableState - // that it will take into account the padding of all drawables specified in a - // StateListDrawable, thus adding superfluous padding to drop-down views. - // - // We assume a StateListDrawable will have a drawable for ABOVE_ANCHOR_STATE_SET and - // at least one other drawable, intended for the 'below-anchor state'. - if (mBackground instanceof StateListDrawable) { - StateListDrawable background = (StateListDrawable) mBackground; - - // Find the above-anchor view - this one's easy, it should be labeled as such. - int aboveAnchorStateIndex = background.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET); - - // Now, for the below-anchor view, look for any other drawable specified in the - // StateListDrawable which is not for the above-anchor state and use that. - int count = background.getStateCount(); - int belowAnchorStateIndex = -1; - for (int i = 0; i < count; i++) { - if (i != aboveAnchorStateIndex) { - belowAnchorStateIndex = i; - break; - } - } - - // Store the drawables we found, if we found them. Otherwise, set them both - // to null so that we'll just use refreshDrawableState. - if (aboveAnchorStateIndex != -1 && belowAnchorStateIndex != -1) { - mAboveAnchorBackgroundDrawable = background.getStateDrawable(aboveAnchorStateIndex); - mBelowAnchorBackgroundDrawable = background.getStateDrawable(belowAnchorStateIndex); - } else { - mBelowAnchorBackgroundDrawable = null; - mAboveAnchorBackgroundDrawable = null; - } - } - a.recycle(); } @@ -701,18 +661,7 @@ public class PopupWindow { mAboveAnchor = findDropDownPosition(anchor, p, xoff, yoff); if (mBackground != null) { - // If the background drawable provided was a StateListDrawable with above-anchor - // and below-anchor states, use those. Otherwise rely on refreshDrawableState to - // do the job. - if (mAboveAnchorBackgroundDrawable != null) { - if (mAboveAnchor) { - mPopupView.setBackgroundDrawable(mAboveAnchorBackgroundDrawable); - } else { - mPopupView.setBackgroundDrawable(mBelowAnchorBackgroundDrawable); - } - } else { - mPopupView.refreshDrawableState(); - } + mPopupView.refreshDrawableState(); } if (mHeightMode < 0) p.height = mLastHeight = mHeightMode; @@ -748,18 +697,12 @@ public class PopupWindow { */ private void preparePopup(WindowManager.LayoutParams p) { if (mBackground != null) { - final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams(); - int height = ViewGroup.LayoutParams.FILL_PARENT; - if (layoutParams != null && - layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) { - height = ViewGroup.LayoutParams.WRAP_CONTENT; - } - // when a background is available, we embed the content view // within another view that owns the background drawable PopupViewContainer popupViewContainer = new PopupViewContainer(mContext); PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, height + ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.FILL_PARENT ); popupViewContainer.setBackgroundDrawable(mBackground); popupViewContainer.addView(mContentView, listParams); diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index f646ab5..dd2570a 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -526,7 +526,6 @@ public class ProgressBar extends View { * @see #getProgress() * @see #incrementProgressBy(int) */ - @android.view.RemotableViewMethod public synchronized void setProgress(int progress) { setProgress(progress, false); } diff --git a/core/java/android/widget/ResourceCursorAdapter.java b/core/java/android/widget/ResourceCursorAdapter.java index a5dbd98..9052ae3 100644 --- a/core/java/android/widget/ResourceCursorAdapter.java +++ b/core/java/android/widget/ResourceCursorAdapter.java @@ -46,30 +46,10 @@ public abstract class ResourceCursorAdapter extends CursorAdapter { public ResourceCursorAdapter(Context context, int layout, Cursor c) { super(context, c); mLayout = mDropDownLayout = layout; - mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } /** - * Constructor. - * - * @param context The context where the ListView associated with this - * SimpleListItemFactory is running - * @param layout resource identifier of a layout file that defines the views - * for this list item. Unless you override them later, this will - * define both the item views and the drop down views. - * @param c The cursor from which to get the data. - * @param autoRequery If true the adapter will call requery() on the - * cursor whenever it changes so the most recent - * data is always displayed. - * @hide Pending API Council approval - */ - public ResourceCursorAdapter(Context context, int layout, Cursor c, boolean autoRequery) { - super(context, c, autoRequery); - mLayout = mDropDownLayout = layout; - mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - } - - /** * Inflates view(s) from the specified XML file. * * @see android.widget.CursorAdapter#newView(android.content.Context, diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 88b2a01..c852be5 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -84,7 +84,6 @@ import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewDebug; -import android.view.ViewRoot; import android.view.ViewTreeObserver; import android.view.ViewGroup.LayoutParams; import android.view.animation.AnimationUtils; @@ -216,7 +215,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int mDrawableSizeTop, mDrawableSizeBottom, mDrawableSizeLeft, mDrawableSizeRight; int mDrawableWidthTop, mDrawableWidthBottom, mDrawableHeightLeft, mDrawableHeightRight; int mDrawablePadding; - } + }; private Drawables mDrawables; private CharSequence mError; @@ -240,13 +239,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private int mMarqueeRepeatLimit = 3; class InputContentType { - int imeOptions = EditorInfo.IME_UNDEFINED; - String privateImeOptions; - CharSequence imeActionLabel; - int imeActionId; + String privateContentType; Bundle extras; - OnEditorActionListener onEditorActionListener; - boolean enterDown; } InputContentType mInputContentType; @@ -274,26 +268,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener p.measureText("H"); } - /** - * Interface definition for a callback to be invoked when an action is - * performed on the editor. - */ - public interface OnEditorActionListener { - /** - * Called when an action is being performed. - * - * @param v The view that was clicked. - * @param actionId Identifier of the action. This will be either the - * identifier you supplied, or {@link EditorInfo#IME_UNDEFINED - * EditorInfo.IME_UNDEFINED} if being called due to the enter key - * being pressed. - * @param event If triggered by an enter key, this is the event; - * otherwise, this is null. - * @return Return true if you have consumed the action, else false. - */ - boolean onEditorAction(TextView v, int actionId, KeyEvent event); - } - public TextView(Context context) { this(context, null); } @@ -402,7 +376,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int shadowcolor = 0; float dx = 0, dy = 0, r = 0; boolean password = false; - int inputType = EditorInfo.TYPE_NULL; + int contentType = EditorInfo.TYPE_NULL; int n = a.getIndexCount(); for (int i = 0; i < n; i++) { @@ -636,34 +610,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; case com.android.internal.R.styleable.TextView_inputType: - inputType = a.getInt(attr, mInputType); + contentType = a.getInt(attr, mInputType); break; - case com.android.internal.R.styleable.TextView_imeOptions: - if (mInputContentType == null) { - mInputContentType = new InputContentType(); - } - mInputContentType.imeOptions = a.getInt(attr, - mInputContentType.imeOptions); - break; - - case com.android.internal.R.styleable.TextView_imeActionLabel: - if (mInputContentType == null) { - mInputContentType = new InputContentType(); - } - mInputContentType.imeActionLabel = a.getText(attr); - break; - - case com.android.internal.R.styleable.TextView_imeActionId: - if (mInputContentType == null) { - mInputContentType = new InputContentType(); - } - mInputContentType.imeActionId = a.getInt(attr, - mInputContentType.imeActionId); - break; - - case com.android.internal.R.styleable.TextView_privateImeOptions: - setPrivateImeOptions(a.getString(attr)); + case com.android.internal.R.styleable.TextView_editorPrivateContentType: + setPrivateContentType(a.getString(attr)); break; case com.android.internal.R.styleable.TextView_editorExtras: @@ -681,7 +632,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener BufferType bufferType = BufferType.EDITABLE; - if ((inputType&(EditorInfo.TYPE_MASK_CLASS + if ((contentType&(EditorInfo.TYPE_MASK_CLASS |EditorInfo.TYPE_MASK_VARIATION)) == (EditorInfo.TYPE_CLASS_TEXT |EditorInfo.TYPE_TEXT_VARIATION_PASSWORD)) { @@ -705,57 +656,57 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener throw new RuntimeException(ex); } try { - mInputType = inputType != EditorInfo.TYPE_NULL - ? inputType + mInputType = contentType != EditorInfo.TYPE_NULL + ? contentType : mInput.getInputType(); } catch (IncompatibleClassChangeError e) { mInputType = EditorInfo.TYPE_CLASS_TEXT; } } else if (digits != null) { mInput = DigitsKeyListener.getInstance(digits.toString()); - mInputType = inputType; - } else if (inputType != EditorInfo.TYPE_NULL) { - setInputType(inputType, true); - singleLine = (inputType&(EditorInfo.TYPE_MASK_CLASS + mInputType = contentType; + } else if (contentType != EditorInfo.TYPE_NULL) { + setInputType(contentType, true); + singleLine = (contentType&(EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE)) != (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE); } else if (phone) { mInput = DialerKeyListener.getInstance(); - inputType = EditorInfo.TYPE_CLASS_PHONE; + contentType = EditorInfo.TYPE_CLASS_PHONE; } else if (numeric != 0) { mInput = DigitsKeyListener.getInstance((numeric & SIGNED) != 0, (numeric & DECIMAL) != 0); - inputType = EditorInfo.TYPE_CLASS_NUMBER; + contentType = EditorInfo.TYPE_CLASS_NUMBER; if ((numeric & SIGNED) != 0) { - inputType |= EditorInfo.TYPE_NUMBER_FLAG_SIGNED; + contentType |= EditorInfo.TYPE_NUMBER_FLAG_SIGNED; } if ((numeric & DECIMAL) != 0) { - inputType |= EditorInfo.TYPE_NUMBER_FLAG_DECIMAL; + contentType |= EditorInfo.TYPE_NUMBER_FLAG_DECIMAL; } - mInputType = inputType; + mInputType = contentType; } else if (autotext || autocap != -1) { TextKeyListener.Capitalize cap; - inputType = EditorInfo.TYPE_CLASS_TEXT; + contentType = EditorInfo.TYPE_CLASS_TEXT; if (!singleLine) { - inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; + contentType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; } switch (autocap) { case 1: cap = TextKeyListener.Capitalize.SENTENCES; - inputType |= EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES; + contentType |= EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES; break; case 2: cap = TextKeyListener.Capitalize.WORDS; - inputType |= EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS; + contentType |= EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS; break; case 3: cap = TextKeyListener.Capitalize.CHARACTERS; - inputType |= EditorInfo.TYPE_TEXT_FLAG_CAP_CHARACTERS; + contentType |= EditorInfo.TYPE_TEXT_FLAG_CAP_CHARACTERS; break; default: @@ -764,7 +715,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } mInput = TextKeyListener.getInstance(autotext, cap); - mInputType = inputType; + mInputType = contentType; } else if (editable) { mInput = TextKeyListener.getInstance(); mInputType = EditorInfo.TYPE_CLASS_TEXT; @@ -1124,11 +1075,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_singleLine */ public final void setTransformationMethod(TransformationMethod method) { - if (method == mTransformation) { - // Avoid the setText() below if the transformation is - // the same. - return; - } if (mTransformation != null) { if (mText instanceof Spannable) { ((Spannable) mText).removeSpan(mTransformation); @@ -2832,7 +2778,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Directly change the content type integer of the text view, without * modifying any other state. - * @see #setInputType(int) + * @see #setContentType * @see android.text.InputType * @attr ref android.R.styleable#TextView_inputType */ @@ -2896,159 +2842,28 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Change the editor type integer associated with the text view, which - * will be reported to an IME with {@link EditorInfo#imeOptions} when it - * has focus. - * @see #getImeOptions - * @see android.view.inputmethod.EditorInfo - * @attr ref android.R.styleable#TextView_imeOptions - */ - public void setImeOptions(int imeOptions) { - if (mInputContentType == null) { - mInputContentType = new InputContentType(); - } - mInputContentType.imeOptions = imeOptions; - } - - /** - * Get the type of the IME editor. - * - * @see #setImeOptions(int) - * @see android.view.inputmethod.EditorInfo - */ - public int getImeOptions() { - return mInputContentType != null - ? mInputContentType.imeOptions : EditorInfo.IME_UNDEFINED; - } - - /** - * Change the custom IME action associated with the text view, which - * will be reported to an IME with {@link EditorInfo#actionLabel} - * and {@link EditorInfo#actionId} when it has focus. - * @see #getImeActionLabel - * @see #getImeActionId - * @see android.view.inputmethod.EditorInfo - * @attr ref android.R.styleable#TextView_imeActionLabel - * @attr ref android.R.styleable#TextView_imeActionId - */ - public void setImeActionLabel(CharSequence label, int actionId) { - if (mInputContentType == null) { - mInputContentType = new InputContentType(); - } - mInputContentType.imeActionLabel = label; - mInputContentType.imeActionId = actionId; - } - - /** - * Get the IME action label previous set with {@link #setImeActionLabel}. - * - * @see #setImeActionLabel - * @see android.view.inputmethod.EditorInfo - */ - public CharSequence getImeActionLabel() { - return mInputContentType != null - ? mInputContentType.imeActionLabel : null; - } - - /** - * Get the IME action ID previous set with {@link #setImeActionLabel}. - * - * @see #setImeActionLabel - * @see android.view.inputmethod.EditorInfo - */ - public int getImeActionId() { - return mInputContentType != null - ? mInputContentType.imeActionId : 0; - } - - /** - * Set a special OnClickListener to be called when an action is performed - * on the text view. This will be called when the enter key is pressed, - * or when an action supplied to the IME is selected by the user. - */ - public void setOnEditorActionListener(OnEditorActionListener l) { - if (mInputContentType == null) { - mInputContentType = new InputContentType(); - } - mInputContentType.onEditorActionListener = l; - } - - /** - * Called when an attached input method calls - * {@link InputConnection#performEditorAction(int) - * InputConnection.performEditorAction()} - * for this text view. The default implementation will call your click - * listener supplied to {@link #setOnEditorActionListener}, - * or generate an enter key down/up pair to invoke the action if not. - * - * @param actionCode The code of the action being performed. - * - * @see #setOnEditorActionListener - */ - public void onEditorAction(int actionCode) { - final InputContentType ict = mInputContentType; - if (ict != null) { - if (ict.onEditorActionListener != null) { - if (ict.onEditorActionListener.onEditorAction(this, - actionCode, null)) { - return; - } - } - } - - if (actionCode == EditorInfo.IME_ACTION_NEXT && - (ict != null || !shouldAdvanceFocusOnEnter())) { - // This is the default handling for the NEXT action, to advance - // focus. Note that for backwards compatibility we don't do this - // default handling if explicit ime options have not been given, - // and we do not advance by default on an enter key -- in that - // case, we want to turn this into the normal enter key codes that - // an app may be expecting. - View v = focusSearch(FOCUS_DOWN); - if (v != null) { - if (!v.requestFocus(FOCUS_DOWN)) { - throw new IllegalStateException("focus search returned a view " + - "that wasn't able to take focus!"); - } - } - return; - } - - Handler h = getHandler(); - long eventTime = SystemClock.uptimeMillis(); - h.sendMessage(h.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME, - new KeyEvent(eventTime, eventTime, - KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0, - KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE))); - h.sendMessage(h.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME, - new KeyEvent(SystemClock.uptimeMillis(), eventTime, - KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0, - KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE))); - } - - /** * Set the private content type of the text, which is the - * {@link EditorInfo#privateImeOptions EditorInfo.privateImeOptions} + * {@link EditorInfo#privateContentType TextBoxAttribute.privateContentType} * field that will be filled in when creating an input connection. * - * @see #getPrivateImeOptions() - * @see EditorInfo#privateImeOptions - * @attr ref android.R.styleable#TextView_privateImeOptions + * @see #getPrivateContentType() + * @see EditorInfo#privateContentType + * @attr ref android.R.styleable#TextView_editorPrivateContentType */ - public void setPrivateImeOptions(String type) { + public void setPrivateContentType(String type) { if (mInputContentType == null) mInputContentType = new InputContentType(); - mInputContentType.privateImeOptions = type; + mInputContentType.privateContentType = type; } /** * Get the private type of the content. * - * @see #setPrivateImeOptions(String) - * @see EditorInfo#privateImeOptions + * @see #setPrivateContentType(String) + * @see EditorInfo#privateContentType */ - public String getPrivateImeOptions() { + public String getPrivateContentType() { return mInputContentType != null - ? mInputContentType.privateImeOptions : null; + ? mInputContentType.privateContentType : null; } /** @@ -3992,7 +3807,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * but also in mail addresses and subjects which will display on multiple * lines but where it doesn't make sense to insert newlines. */ - protected boolean shouldAdvanceFocusOnEnter() { + private boolean advanceFocusOnEnter() { if (mInput == null) { return false; } @@ -4013,37 +3828,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return false; } - private boolean isInterestingEnter(KeyEvent event) { - if ((event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 && - mInputContentType != null && - (mInputContentType.imeOptions & - EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) { - // If this enter key came from a soft keyboard, and the - // text editor has been configured to not do a default - // action for software enter keys, then we aren't interested. - return false; - } - return true; - } - private int doKeyDown(int keyCode, KeyEvent event, KeyEvent otherEvent) { if (!isEnabled()) { return 0; } switch (keyCode) { - case KeyEvent.KEYCODE_ENTER: - if (!isInterestingEnter(event)) { - // Ignore enter key we aren't interested in. - return -1; - } - if (mInputContentType != null - && mInputContentType.onEditorActionListener != null) { - mInputContentType.enterDown = true; - } - // fall through... case KeyEvent.KEYCODE_DPAD_CENTER: - if (shouldAdvanceFocusOnEnter()) { + case KeyEvent.KEYCODE_ENTER: + if (advanceFocusOnEnter()) { return 0; } } @@ -4146,17 +3939,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return super.onKeyUp(keyCode, event); case KeyEvent.KEYCODE_ENTER: - if (mInputContentType != null - && mInputContentType.onEditorActionListener != null - && mInputContentType.enterDown) { - mInputContentType.enterDown = false; - if (mInputContentType.onEditorActionListener.onEditorAction( - this, EditorInfo.IME_UNDEFINED, event)) { - return true; - } - } - - if (shouldAdvanceFocusOnEnter()) { + if (advanceFocusOnEnter()) { /* * If there is a click listener, just call through to * super, which will invoke it. @@ -4211,26 +3994,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mInputMethodState = new InputMethodState(); } outAttrs.inputType = mInputType; + outAttrs.hintText = mHint; if (mInputContentType != null) { - outAttrs.imeOptions = mInputContentType.imeOptions; - outAttrs.privateImeOptions = mInputContentType.privateImeOptions; - outAttrs.actionLabel = mInputContentType.imeActionLabel; - outAttrs.actionId = mInputContentType.imeActionId; + outAttrs.privateContentType = mInputContentType.privateContentType; outAttrs.extras = mInputContentType.extras; - } else { - outAttrs.imeOptions = EditorInfo.IME_UNDEFINED; - } - if (outAttrs.imeOptions == EditorInfo.IME_UNDEFINED) { - if (focusSearch(FOCUS_DOWN) != null) { - // An action has not been set, but the enter key will move to - // the next focus, so set the action to that. - outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT; - if (!shouldAdvanceFocusOnEnter()) { - outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION; - } - } } - outAttrs.hintText = mHint; if (mText instanceof Editable) { InputConnection ic = new EditableInputConnection(this); outAttrs.initialSelStart = Selection.getSelectionStart(mText); @@ -6019,9 +5787,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } // Don't leave us in the middle of a batch edit. onEndBatchEdit(); - if (mInputContentType != null) { - mInputContentType.enterDown = false; - } } startStopMarquee(hasWindowFocus); @@ -6115,7 +5880,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mScroller = s; } - private static class Blink extends Handler implements Runnable { + private static class Blink extends Handler + implements Runnable { private WeakReference<TextView> mView; private boolean mCancelled; @@ -6373,44 +6139,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } int start = end; + char c; int len = mText.length(); - for (; start > 0; start--) { - char c = mTransformed.charAt(start - 1); - int type = Character.getType(c); - - if (c != '\'' && - type != Character.UPPERCASE_LETTER && - type != Character.LOWERCASE_LETTER && - type != Character.TITLECASE_LETTER && - type != Character.MODIFIER_LETTER && - type != Character.DECIMAL_DIGIT_NUMBER) { - break; - } + while (start > 0 && (((c = mTransformed.charAt(start - 1)) == '\'') || + (Character.isLetterOrDigit(c)))) { + start--; } - for (; end < len; end++) { - char c = mTransformed.charAt(end); - int type = Character.getType(c); - - if (c != '\'' && - type != Character.UPPERCASE_LETTER && - type != Character.LOWERCASE_LETTER && - type != Character.TITLECASE_LETTER && - type != Character.MODIFIER_LETTER && - type != Character.DECIMAL_DIGIT_NUMBER) { - break; - } + while (end < len && (((c = mTransformed.charAt(end)) == '\'') || + (Character.isLetterOrDigit(c)))) { + end++; } if (start == end) { return null; } - if (end - start > 48) { - return null; - } - return TextUtils.substring(mTransformed, start, end); } diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java deleted file mode 100644 index ec45e23..0000000 --- a/core/java/android/widget/ZoomButtonsController.java +++ /dev/null @@ -1,478 +0,0 @@ -/* - * Copyright (C) 2008 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.widget; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.os.Handler; -import android.os.Message; -import android.provider.Settings; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.WindowManager; -import android.view.View.OnClickListener; -import android.view.WindowManager.LayoutParams; - -// TODO: make sure no px values exist, only dip (scale if necessary from Viewconfiguration) - -/** - * TODO: Docs - * - * If you are using this with a custom View, please call - * {@link #setVisible(boolean) setVisible(false)} from the - * {@link View#onDetachedFromWindow}. - * - * @hide - */ -public class ZoomButtonsController implements View.OnTouchListener { - - private static final String TAG = "ZoomButtonsController"; - - private static final int ZOOM_CONTROLS_TIMEOUT = - (int) ViewConfiguration.getZoomControlsTimeout(); - - // TODO: scaled to density - private static final int ZOOM_CONTROLS_TOUCH_PADDING = 20; - - private Context mContext; - private WindowManager mWindowManager; - - /** - * The view that is being zoomed by this zoom ring. - */ - private View mOwnerView; - - /** - * The bounds of the owner view in global coordinates. This is recalculated - * each time the zoom ring is shown. - */ - private Rect mOwnerViewBounds = new Rect(); - - /** - * The container that is added as a window. - */ - private FrameLayout mContainer; - private LayoutParams mContainerLayoutParams; - private int[] mContainerLocation = new int[2]; - - private ZoomControls mControls; - - /** - * The view (or null) that should receive touch events. This will get set if - * the touch down hits the container. It will be reset on the touch up. - */ - private View mTouchTargetView; - /** - * The {@link #mTouchTargetView}'s location in window, set on touch down. - */ - private int[] mTouchTargetLocationInWindow = new int[2]; - /** - * If the zoom ring is dismissed but the user is still in a touch - * interaction, we set this to true. This will ignore all touch events until - * up/cancel, and then set the owner's touch listener to null. - */ - private boolean mReleaseTouchListenerOnUp; - - private boolean mIsVisible; - - private Rect mTempRect = new Rect(); - - private OnZoomListener mCallback; - - /** - * When showing the zoom, we add the view as a new window. However, there is - * logic that needs to know the size of the zoom which is determined after - * it's laid out. Therefore, we must post this logic onto the UI thread so - * it will be exceuted AFTER the layout. This is the logic. - */ - private Runnable mPostedVisibleInitializer; - - private IntentFilter mConfigurationChangedFilter = - new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED); - - private BroadcastReceiver mConfigurationChangedReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (!mIsVisible) return; - - mHandler.removeMessages(MSG_POST_CONFIGURATION_CHANGED); - mHandler.sendEmptyMessage(MSG_POST_CONFIGURATION_CHANGED); - } - }; - - /** When configuration changes, this is called after the UI thread is idle. */ - private static final int MSG_POST_CONFIGURATION_CHANGED = 2; - /** Used to delay the zoom ring dismissal. */ - private static final int MSG_DISMISS_ZOOM_RING = 3; - /** - * If setVisible(true) is called and the owner view's window token is null, - * we delay the setVisible(true) call until it is not null. - */ - private static final int MSG_POST_SET_VISIBLE = 4; - - private Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_POST_CONFIGURATION_CHANGED: - onPostConfigurationChanged(); - break; - - case MSG_DISMISS_ZOOM_RING: - setVisible(false); - break; - - case MSG_POST_SET_VISIBLE: - if (mOwnerView.getWindowToken() == null) { - // Doh, it is still null, throw an exception - throw new IllegalArgumentException( - "Cannot make the zoom ring visible if the owner view is " + - "not attached to a window."); - } - setVisible(true); - break; - } - - } - }; - - public ZoomButtonsController(Context context, View ownerView) { - mContext = context; - mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - mOwnerView = ownerView; - - mContainer = createContainer(); - } - - private FrameLayout createContainer() { - LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.BOTTOM | Gravity.CENTER; - lp.flags = LayoutParams.FLAG_NOT_TOUCHABLE | - LayoutParams.FLAG_LAYOUT_NO_LIMITS; - lp.height = LayoutParams.WRAP_CONTENT; - lp.width = LayoutParams.FILL_PARENT; - lp.type = LayoutParams.TYPE_APPLICATION_PANEL; - lp.format = PixelFormat.TRANSPARENT; - // TODO: make a new animation for this - lp.windowAnimations = com.android.internal.R.style.Animation_InputMethodFancy; - mContainerLayoutParams = lp; - - FrameLayout container = new FrameLayout(mContext); - container.setLayoutParams(lp); - container.setMeasureAllChildren(true); - - LayoutInflater inflater = (LayoutInflater) mContext - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater.inflate(com.android.internal.R.layout.zoom_magnify, container); - - mControls = (ZoomControls) container.findViewById(com.android.internal.R.id.zoomControls); - mControls.setOnZoomInClickListener(new OnClickListener() { - public void onClick(View v) { - dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT); - if (mCallback != null) mCallback.onZoom(true); - } - }); - mControls.setOnZoomOutClickListener(new OnClickListener() { - public void onClick(View v) { - dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT); - if (mCallback != null) mCallback.onZoom(false); - } - }); - - View overview = container.findViewById(com.android.internal.R.id.zoomMagnify); - overview.setVisibility(View.GONE); - overview.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT); - if (mCallback != null) mCallback.onOverview(); - } - }); - - return container; - } - - public void setCallback(OnZoomListener callback) { - mCallback = callback; - } - - public void setFocusable(boolean focusable) { - if (focusable) { - mContainerLayoutParams.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE; - } else { - mContainerLayoutParams.flags |= LayoutParams.FLAG_NOT_FOCUSABLE; - } - - if (mIsVisible) { - mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams); - } - } - - public void setOverviewVisible(boolean visible) { - mContainer.findViewById(com.android.internal.R.id.zoomMagnify) - .setVisibility(visible ? View.VISIBLE : View.GONE); - } - - public boolean isVisible() { - return mIsVisible; - } - - public void setVisible(boolean visible) { - - if (!useThisZoom(mContext)) return; - - if (visible) { - if (mOwnerView.getWindowToken() == null) { - /* - * We need a window token to show ourselves, maybe the owner's - * window hasn't been created yet but it will have been by the - * time the looper is idle, so post the setVisible(true) call. - */ - if (!mHandler.hasMessages(MSG_POST_SET_VISIBLE)) { - mHandler.sendEmptyMessage(MSG_POST_SET_VISIBLE); - } - return; - } - - dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT); - } - - if (mIsVisible == visible) { - return; - } - mIsVisible = visible; - - if (visible) { - if (mContainerLayoutParams.token == null) { - mContainerLayoutParams.token = mOwnerView.getWindowToken(); - } - - mWindowManager.addView(mContainer, mContainerLayoutParams); - - if (mPostedVisibleInitializer == null) { - mPostedVisibleInitializer = new Runnable() { - public void run() { - refreshPositioningVariables(); - - if (mCallback != null) { - mCallback.onVisibilityChanged(true); - } - } - }; - } - - mHandler.post(mPostedVisibleInitializer); - - // Handle configuration changes when visible - mContext.registerReceiver(mConfigurationChangedReceiver, mConfigurationChangedFilter); - - // Steal touches events from the owner - mOwnerView.setOnTouchListener(this); - mReleaseTouchListenerOnUp = false; - - } else { - // Don't want to steal any more touches - if (mTouchTargetView != null) { - // We are still stealing the touch events for this touch - // sequence, so release the touch listener later - mReleaseTouchListenerOnUp = true; - } else { - mOwnerView.setOnTouchListener(null); - } - - // No longer care about configuration changes - mContext.unregisterReceiver(mConfigurationChangedReceiver); - - mWindowManager.removeView(mContainer); - mHandler.removeCallbacks(mPostedVisibleInitializer); - - if (mCallback != null) { - mCallback.onVisibilityChanged(false); - } - } - - } - - /** - * TODO: docs - * - * Notes: - * - Please ensure you set your View to INVISIBLE not GONE when hiding it. - * - * @return TODO - */ - public FrameLayout getContainer() { - return mContainer; - } - - public int getZoomRingId() { - return mControls.getId(); - } - - private void dismissControlsDelayed(int delay) { - mHandler.removeMessages(MSG_DISMISS_ZOOM_RING); - mHandler.sendEmptyMessageDelayed(MSG_DISMISS_ZOOM_RING, delay); - } - - /** - * Should be called by the client for each event belonging to the second tap - * (the down, move, up, and cancel events). - * - * @param event The event belonging to the second tap. - * @return Whether the event was consumed. - */ - public boolean handleDoubleTapEvent(MotionEvent event) { - if (!useThisZoom(mContext)) return false; - - int action = event.getAction(); - - if (action == MotionEvent.ACTION_DOWN) { - int x = (int) event.getX(); - int y = (int) event.getY(); - - setVisible(true); - centerPoint(x, y); - } - - return true; - } - - private void refreshPositioningVariables() { - // Calculate the owner view's bounds - mOwnerView.getGlobalVisibleRect(mOwnerViewBounds); - mContainer.getLocationOnScreen(mContainerLocation); - } - - /** - * Centers the point (in owner view's coordinates). - */ - private void centerPoint(int x, int y) { - if (mCallback != null) { - mCallback.onCenter(x, y); - } - } - - public boolean onTouch(View v, MotionEvent event) { - int action = event.getAction(); - - if (mReleaseTouchListenerOnUp) { - // The ring was dismissed but we need to throw away all events until the up - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { - mOwnerView.setOnTouchListener(null); - setTouchTargetView(null); - mReleaseTouchListenerOnUp = false; - } - - // Eat this event - return true; - } - - // TODO: optimize this (it ends up removing message and queuing another) - dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT); - - View targetView = mTouchTargetView; - - switch (action) { - case MotionEvent.ACTION_DOWN: - targetView = getViewForTouch((int) event.getRawX(), (int) event.getRawY()); - setTouchTargetView(targetView); - break; - - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - setTouchTargetView(null); - break; - } - - if (targetView != null) { - // The upperleft corner of the target view in raw coordinates - int targetViewRawX = mContainerLocation[0] + mTouchTargetLocationInWindow[0]; - int targetViewRawY = mContainerLocation[1] + mTouchTargetLocationInWindow[1]; - - MotionEvent containerEvent = MotionEvent.obtain(event); - // Convert the motion event into the target view's coordinates (from - // owner view's coordinates) - containerEvent.offsetLocation(mOwnerViewBounds.left - targetViewRawX, - mOwnerViewBounds.top - targetViewRawY); - boolean retValue = targetView.dispatchTouchEvent(containerEvent); - containerEvent.recycle(); - return retValue; - - } else { - return false; - } - } - - private void setTouchTargetView(View view) { - mTouchTargetView = view; - if (view != null) { - view.getLocationInWindow(mTouchTargetLocationInWindow); - } - } - - /** - * Returns the View that should receive a touch at the given coordinates. - * - * @param rawX The raw X. - * @param rawY The raw Y. - * @return The view that should receive the touches, or null if there is not one. - */ - private View getViewForTouch(int rawX, int rawY) { - // Reverse order so the child drawn on top gets first dibs. - int containerCoordsX = rawX - mContainerLocation[0]; - int containerCoordsY = rawY - mContainerLocation[1]; - Rect frame = mTempRect; - for (int i = mContainer.getChildCount() - 1; i >= 0; i--) { - View child = mContainer.getChildAt(i); - if (child.getVisibility() != View.VISIBLE) { - continue; - } - - child.getHitRect(frame); - // Expand the touch region - frame.top -= ZOOM_CONTROLS_TOUCH_PADDING; - if (frame.contains(containerCoordsX, containerCoordsY)) { - return child; - } - } - - return null; - } - - private void onPostConfigurationChanged() { - dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT); - refreshPositioningVariables(); - } - - public static boolean useThisZoom(Context context) { - return ZoomRingController.getZoomType(context) == 2; - } - - public interface OnZoomListener { - void onCenter(int x, int y); - void onVisibilityChanged(boolean visible); - void onZoom(boolean zoomIn); - void onOverview(); - } -} diff --git a/core/java/android/widget/ZoomRing.java b/core/java/android/widget/ZoomRing.java index a5a867b..a29e1a0 100644 --- a/core/java/android/widget/ZoomRing.java +++ b/core/java/android/widget/ZoomRing.java @@ -77,7 +77,7 @@ public class ZoomRing extends View { private int mPreviousWidgetDragX; private int mPreviousWidgetDragY; - private boolean mThumbVisible = true; + private boolean mDrawThumb = true; private Drawable mThumbDrawable; /** Shown beneath the thumb if we can still zoom in. */ @@ -91,13 +91,6 @@ public class ZoomRing extends View { private static final int THUMB_ARROWS_FADE_DURATION = 300; private long mThumbArrowsFadeStartTime; private int mThumbArrowsAlpha = 255; - - private static final int THUMB_PLUS_MINUS_DISTANCE = 69; - private static final int THUMB_PLUS_MINUS_OFFSET_ANGLE = TWO_PI_INT_MULTIPLIED / 11; - /** Drawn (without rotation) on top of the arrow. */ - private Drawable mThumbPlusDrawable; - /** Drawn (without rotation) on top of the arrow. */ - private Drawable mThumbMinusDrawable; private static final int MODE_IDLE = 0; @@ -106,7 +99,7 @@ public class ZoomRing extends View { * are waiting for him to move the slop amount before considering him in the * drag thumb state. */ - private static final int MODE_WAITING_FOR_DRAG_THUMB_AFTER_JUMP = 5; + private static final int MODE_WAITING_FOR_DRAG_THUMB = 5; private static final int MODE_DRAG_THUMB = 1; /** * User has his finger down, but we are waiting for him to pass the touch @@ -116,14 +109,11 @@ public class ZoomRing extends View { private static final int MODE_WAITING_FOR_MOVE_ZOOM_RING = 4; private static final int MODE_MOVE_ZOOM_RING = 2; private static final int MODE_TAP_DRAG = 3; - /** Ignore the touch interaction until the user touches the thumb again. */ - private static final int MODE_IGNORE_UNTIL_TOUCHES_THUMB = 6; + /** Ignore the touch interaction. Reset to MODE_IDLE after up/cancel. */ + private static final int MODE_IGNORE_UNTIL_UP = 6; private int mMode; - - /** Records the last mode the user was in. */ - private int mPreviousMode; - - private long mPreviousCenterUpTime; + + private long mPreviousUpTime; private int mPreviousDownX; private int mPreviousDownY; @@ -132,9 +122,7 @@ public class ZoomRing extends View { private OnZoomRingCallback mCallback; private int mPreviousCallbackAngle; private int mCallbackThreshold = Integer.MAX_VALUE; - /** If the user drags to within __% of a tick, snap to that tick. */ - private int mFuzzyCallbackThreshold = Integer.MAX_VALUE; - + private boolean mResetThumbAutomatically = true; private int mThumbDragStartAngle; @@ -145,8 +133,6 @@ public class ZoomRing extends View { private Scroller mThumbScroller; - private boolean mVibration = true; - private static final int MSG_THUMB_SCROLLER_TICK = 1; private static final int MSG_THUMB_ARROWS_FADE_TICK = 2; private Handler mHandler = new Handler() { @@ -177,8 +163,6 @@ public class ZoomRing extends View { mutate(); mThumbMinusArrowDrawable = res.getDrawable(R.drawable.zoom_ring_thumb_minus_arrow_rotatable). mutate(); - mThumbPlusDrawable = res.getDrawable(R.drawable.zoom_ring_thumb_plus); - mThumbMinusDrawable = res.getDrawable(R.drawable.zoom_ring_thumb_minus); if (DRAW_TRAIL) { mTrail = res.getDrawable(R.drawable.zoom_ring_trail).mutate(); } @@ -191,7 +175,7 @@ public class ZoomRing extends View { mThumbHalfHeight = mThumbDrawable.getIntrinsicHeight() / 2; mThumbHalfWidth = mThumbDrawable.getIntrinsicWidth() / 2; - setCallbackThreshold(PI_INT_MULTIPLIED / 6); + mCallbackThreshold = PI_INT_MULTIPLIED / 6; } public ZoomRing(Context context, AttributeSet attrs) { @@ -209,20 +193,8 @@ public class ZoomRing extends View { // TODO: rename public void setCallbackThreshold(int callbackThreshold) { mCallbackThreshold = callbackThreshold; - mFuzzyCallbackThreshold = (int) (callbackThreshold * 0.65f); } - public void setVibration(boolean vibrate) { - mVibration = vibrate; - } - - public void setThumbVisible(boolean thumbVisible) { - if (mThumbVisible != thumbVisible) { - mThumbVisible = thumbVisible; - invalidate(); - } - } - // TODO: from XML too public void setRingBounds(int innerRadius, int outerRadius) { mBoundInnerRadiusSquared = innerRadius * innerRadius; @@ -334,7 +306,15 @@ public class ZoomRing extends View { public void setThumbAngleAnimated(int angle, int duration) { // The angle when going from the current angle to the new angle int deltaAngle = getDelta(mThumbAngle, angle); - setThumbAngleAnimated(angle, duration, deltaAngle > 0); + // Counter clockwise if the new angle is more the current angle + boolean counterClockwise = deltaAngle > 0; + + if (deltaAngle > PI_INT_MULTIPLIED || deltaAngle < -PI_INT_MULTIPLIED) { + // It's quicker to go the other direction + counterClockwise = !counterClockwise; + } + + setThumbAngleAnimated(angle, duration, counterClockwise); } public void setThumbAngleAnimated(int angle, int duration, boolean counterClockwise) { @@ -374,10 +354,14 @@ public class ZoomRing extends View { return mThumbScroller.getCurrX() % TWO_PI_INT_MULTIPLIED; } + public void resetThumbAngle(int angle) { + mPreviousCallbackAngle = angle; + setThumbAngleInt(angle); + } + public void resetThumbAngle() { if (mResetThumbAutomatically) { - mPreviousCallbackAngle = 0; - setThumbAngleInt(0); + resetThumbAngle(0); } } @@ -410,119 +394,101 @@ public class ZoomRing extends View { mTrail.setBounds(0, 0, right - left, bottom - top); } - // These drawables are the same size as the track mThumbPlusArrowDrawable.setBounds(0, 0, right - left, bottom - top); mThumbMinusArrowDrawable.setBounds(0, 0, right - left, bottom - top); } @Override public boolean onTouchEvent(MotionEvent event) { -// Log.d(TAG, "History size: " + event.getHistorySize()); - return handleTouch(event.getAction(), event.getEventTime(), (int) event.getX(), (int) event.getY(), (int) event.getRawX(), (int) event.getRawY()); } - private void resetToIdle() { - setMode(MODE_IDLE); + private void resetState() { + mMode = MODE_IDLE; mPreviousWidgetDragX = mPreviousWidgetDragY = Integer.MIN_VALUE; mAcculumalatedTrailAngle = 0.0; } public void setTapDragMode(boolean tapDragMode, int x, int y) { - resetToIdle(); + resetState(); + mMode = tapDragMode ? MODE_TAP_DRAG : MODE_IDLE; + if (tapDragMode) { - setMode(MODE_TAP_DRAG); - mCallback.onUserInteractionStarted(); onThumbDragStarted(getAngle(x - mCenterX, y - mCenterY)); - } else { - onTouchUp(SystemClock.elapsedRealtime(), true); } } public boolean handleTouch(int action, long time, int x, int y, int rawX, int rawY) { - // local{X,Y} will be where the center of the widget is (0,0) - int localX = x - mCenterX; - int localY = y - mCenterY; - - /* - * If we are not drawing the thumb, there is no way for the user to be - * touching the thumb. Also, if this is the case, assume they are not - * touching the ring (so the user cannot absolute set the thumb, and - * there will be a larger touch region for going into the move-ring - * mode). - */ - boolean isTouchingThumb = mThumbVisible; - boolean isTouchingRing = mThumbVisible; - - int touchAngle = getAngle(localX, localY); -// printAngle("touchAngle", touchAngle); -// printAngle("mThumbAngle", mThumbAngle); -// printAngle("mPreviousCallbackAngle", mPreviousCallbackAngle); -// Log.d(TAG, ""); - - - int radiusSquared = localX * localX + localY * localY; - if (radiusSquared < mBoundInnerRadiusSquared || - radiusSquared > mBoundOuterRadiusSquared) { - // Out-of-bounds - isTouchingThumb = false; - isTouchingRing = false; - } - - if (isTouchingThumb) { - int deltaThumbAndTouch = getDelta(mThumbAngle, touchAngle); - int absoluteDeltaThumbAndTouch = deltaThumbAndTouch >= 0 ? - deltaThumbAndTouch : -deltaThumbAndTouch; - if (absoluteDeltaThumbAndTouch > THUMB_GRAB_SLOP) { - // Didn't grab close enough to the thumb - isTouchingThumb = false; - } - } - switch (action) { + case MotionEvent.ACTION_DOWN: - if (!isTouchingRing && - (time - mPreviousCenterUpTime <= DOUBLE_TAP_DISMISS_TIMEOUT)) { - // Make sure the double-tap is in the center of the widget (and not on the ring) + if (time - mPreviousUpTime <= DOUBLE_TAP_DISMISS_TIMEOUT) { mCallback.onZoomRingDismissed(true); - onTouchUp(time, isTouchingRing); + onTouchUp(time); // Dismissing, so halt here return true; } - resetToIdle(); mCallback.onUserInteractionStarted(); mPreviousDownX = x; mPreviousDownY = y; + resetState(); // Fall through to code below switch (since the down is used for // jumping to the touched tick) break; case MotionEvent.ACTION_MOVE: + if (mMode == MODE_IGNORE_UNTIL_UP) return true; + // Fall through to code below switch break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: - onTouchUp(time, isTouchingRing); + onTouchUp(time); return true; default: return false; } + // local{X,Y} will be where the center of the widget is (0,0) + int localX = x - mCenterX; + int localY = y - mCenterY; + boolean isTouchingThumb = true; + boolean isInRingBounds = true; + + int touchAngle = getAngle(localX, localY); + int radiusSquared = localX * localX + localY * localY; + if (radiusSquared < mBoundInnerRadiusSquared || + radiusSquared > mBoundOuterRadiusSquared) { + // Out-of-bounds + isTouchingThumb = false; + isInRingBounds = false; + } + + int deltaThumbAndTouch = getDelta(mThumbAngle, touchAngle); + int absoluteDeltaThumbAndTouch = deltaThumbAndTouch >= 0 ? + deltaThumbAndTouch : -deltaThumbAndTouch; + if (isTouchingThumb && + absoluteDeltaThumbAndTouch > THUMB_GRAB_SLOP) { + // Didn't grab close enough to the thumb + isTouchingThumb = false; + } + if (mMode == MODE_IDLE) { if (isTouchingThumb) { // They grabbed the thumb - setMode(MODE_DRAG_THUMB); + mMode = MODE_DRAG_THUMB; onThumbDragStarted(touchAngle); - } else if (isTouchingRing) { + } else if (isInRingBounds) { // They tapped somewhere else on the ring int tickAngle = getClosestTickAngle(touchAngle); + int deltaThumbAndTick = getDelta(mThumbAngle, tickAngle); int boundAngle = getBoundIfExceeds(mThumbAngle, deltaThumbAndTick); @@ -531,12 +497,12 @@ public class ZoomRing extends View { if (deltaThumbAndTick > MAX_ABS_JUMP_DELTA_ANGLE || deltaThumbAndTick < -MAX_ABS_JUMP_DELTA_ANGLE) { // Trying to jump too far, ignore this touch interaction - setMode(MODE_IGNORE_UNTIL_TOUCHES_THUMB); + mMode = MODE_IGNORE_UNTIL_UP; return true; } + // Make sure we only let them jump within bounds if (boundAngle != Integer.MIN_VALUE) { - // Cap the user's jump to the bound tickAngle = boundAngle; } } else { @@ -549,59 +515,47 @@ public class ZoomRing extends View { deltaThumbAndTick = getDelta(mThumbAngle, tickAngle, !oldDirectionIsCcw); boundAngle = getBoundIfExceeds(mThumbAngle, deltaThumbAndTick); if (boundAngle != Integer.MIN_VALUE) { - // Cannot get to the tapped location because it is out-of-bounds - setMode(MODE_IGNORE_UNTIL_TOUCHES_THUMB); + // Not allowed to be here, it is between two bounds + mMode = MODE_IGNORE_UNTIL_UP; return true; } } } - setMode(MODE_WAITING_FOR_DRAG_THUMB_AFTER_JUMP); + mMode = MODE_WAITING_FOR_DRAG_THUMB; mWaitingForDragThumbDownAngle = touchAngle; boolean ccw = deltaThumbAndTick > 0; setThumbAngleAnimated(tickAngle, 0, ccw); - /* - * Our thumb scrolling animation takes us from mThumbAngle to - * tickAngle, so manifest that as the user dragging the thumb - * there. - */ + // Our thumb scrolling animation takes us from mThumbAngle to tickAngle onThumbDragStarted(mThumbAngle); - // We know which direction we want to go onThumbDragged(tickAngle, true, ccw); } else { - // They tapped somewhere else on the widget - setMode(MODE_WAITING_FOR_MOVE_ZOOM_RING); + // They tapped somewhere else + mMode = MODE_WAITING_FOR_MOVE_ZOOM_RING; mCallback.onZoomRingSetMovableHintVisible(true); } - } else if (mMode == MODE_WAITING_FOR_DRAG_THUMB_AFTER_JUMP) { + } else if (mMode == MODE_WAITING_FOR_DRAG_THUMB) { int deltaDownAngle = getDelta(mWaitingForDragThumbDownAngle, touchAngle); if ((deltaDownAngle < -THUMB_DRAG_SLOP || deltaDownAngle > THUMB_DRAG_SLOP) && isDeltaInBounds(mWaitingForDragThumbDownAngle, deltaDownAngle)) { - setMode(MODE_DRAG_THUMB); - - // No need to call onThumbDragStarted, since that was done when they tapped-to-jump + mMode = MODE_DRAG_THUMB; } } else if (mMode == MODE_WAITING_FOR_MOVE_ZOOM_RING) { if (Math.abs(x - mPreviousDownX) > mTouchSlop || Math.abs(y - mPreviousDownY) > mTouchSlop) { /* Make sure the user has moved the slop amount before going into that mode. */ - setMode(MODE_MOVE_ZOOM_RING); + mMode = MODE_MOVE_ZOOM_RING; mCallback.onZoomRingMovingStarted(); } - } else if (mMode == MODE_IGNORE_UNTIL_TOUCHES_THUMB) { - if (isTouchingThumb) { - // The user is back on the thumb, let's go back to the previous mode - setMode(mPreviousMode); - } } // Purposefully not an "else if" if (mMode == MODE_DRAG_THUMB || mMode == MODE_TAP_DRAG) { - if (isTouchingRing) { + if (isInRingBounds) { onThumbDragged(touchAngle, false, false); } } else if (mMode == MODE_MOVE_ZOOM_RING) { @@ -611,39 +565,24 @@ public class ZoomRing extends View { return true; } - private void onTouchUp(long time, boolean isTouchingRing) { - int mode = mMode; - if (mode == MODE_IGNORE_UNTIL_TOUCHES_THUMB) { - // For cleaning up, pretend like the user was still in the previous mode - mode = mPreviousMode; - } - - if (mode == MODE_MOVE_ZOOM_RING || mode == MODE_WAITING_FOR_MOVE_ZOOM_RING) { + private void onTouchUp(long time) { + if (mMode == MODE_MOVE_ZOOM_RING || mMode == MODE_WAITING_FOR_MOVE_ZOOM_RING) { mCallback.onZoomRingSetMovableHintVisible(false); - if (mode == MODE_MOVE_ZOOM_RING) { + if (mMode == MODE_MOVE_ZOOM_RING) { mCallback.onZoomRingMovingStopped(); } - } else if (mode == MODE_DRAG_THUMB || mode == MODE_TAP_DRAG || - mode == MODE_WAITING_FOR_DRAG_THUMB_AFTER_JUMP) { + } else if (mMode == MODE_DRAG_THUMB || mMode == MODE_TAP_DRAG || + mMode == MODE_WAITING_FOR_DRAG_THUMB) { onThumbDragStopped(); - if (mode == MODE_DRAG_THUMB || mode == MODE_TAP_DRAG) { + if (mMode == MODE_DRAG_THUMB) { // Animate back to a tick setThumbAngleAnimated(mPreviousCallbackAngle, 0); } } + + mPreviousUpTime = time; mCallback.onUserInteractionStopped(); - - if (!isTouchingRing) { - mPreviousCenterUpTime = time; - } - } - - private void setMode(int mode) { - if (mode != mMode) { - mPreviousMode = mMode; - mMode = mode; - } } private boolean isDeltaInBounds(int startAngle, int deltaAngle) { @@ -742,8 +681,9 @@ public class ZoomRing extends View { int totalDeltaAngle; totalDeltaAngle = getDelta(mPreviousCallbackAngle, touchAngle, useDirection, ccw); - if (totalDeltaAngle >= mFuzzyCallbackThreshold - || totalDeltaAngle <= -mFuzzyCallbackThreshold) { + int fuzzyCallbackThreshold = (int) (mCallbackThreshold * 0.65f); + if (totalDeltaAngle >= fuzzyCallbackThreshold + || totalDeltaAngle <= -fuzzyCallbackThreshold) { if (!useDirection) { // Set ccw to match the direction found by getDelta @@ -797,7 +737,7 @@ public class ZoomRing extends View { // We bounded the touch angle totalDeltaAngle = getDelta(mPreviousCallbackAngle, touchAngle, useDirection, ccw); animateThumbToNewAngle = true; - setMode(MODE_IGNORE_UNTIL_TOUCHES_THUMB); + mMode = MODE_IGNORE_UNTIL_UP; } @@ -824,13 +764,11 @@ public class ZoomRing extends View { boolean canStillZoom = mCallback.onZoomRingThumbDragged( deltaLevels, mThumbDragStartAngle, touchAngle); - if (mVibration) { - // TODO: we're trying the haptics to see how it goes with - // users, so we're ignoring the settings (for now) - performHapticFeedback(HapticFeedbackConstants.ZOOM_RING_TICK, - HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING | - HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); - } + // TODO: we're trying the haptics to see how it goes with + // users, so we're ignoring the settings (for now) + performHapticFeedback(HapticFeedbackConstants.ZOOM_RING_TICK, + HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING | + HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); // Set the callback angle to the actual angle based on how many delta levels we gave mPreviousCallbackAngle = getValidAngle( @@ -853,134 +791,6 @@ public class ZoomRing extends View { setThumbAngleAuto(touchAngle, useDirection, ccw); } } -// private void onThumbDragged(int touchAngle, boolean useDirection, boolean ccw) { -// int deltaPrevCbAndTouch = getDelta(mPreviousCallbackAngle, touchAngle, useDirection, ccw); -// -// if (!useDirection) { -// // Set ccw to match the direction found by getDelta -// ccw = deltaPrevCbAndTouch > 0; -// useDirection = true; -// } -// -// boolean animateThumbToNewAngle = false; -// boolean animationCcw = ccw; -// -// if (deltaPrevCbAndTouch >= mFuzzyCallbackThreshold -// || deltaPrevCbAndTouch <= -mFuzzyCallbackThreshold) { -// -// /* -// * When the user slides the thumb through the tick that corresponds -// * to a zoom bound, we don't want to abruptly stop there. Instead, -// * let the user slide it to the next tick, and then animate it back -// * to the original zoom bound tick. Because of this, we make sure -// * the delta from the bound is more than halfway to the next tick. -// * We make sure the bound is between the touch and the previous -// * callback to ensure we JUST passed the bound. -// */ -// int oldTouchAngle = touchAngle; -// if (ccw && mThumbCcwBound != Integer.MIN_VALUE) { -// int deltaCcwBoundAndTouch = -// getDelta(mThumbCcwBound, touchAngle, true, ccw); -// if (deltaCcwBoundAndTouch >= mCallbackThreshold / 2) { -// // The touch has past far enough from the bound -// int deltaPreviousCbAndTouch = getDelta(mPreviousCallbackAngle, -// touchAngle, true, ccw); -// if (deltaPreviousCbAndTouch >= deltaCcwBoundAndTouch) { -// // The bound is between the previous callback angle and the touch -// // Cap to the bound -// touchAngle = mThumbCcwBound; -// /* -// * We're moving the touch BACK to the bound, so animate -// * back in the opposite direction that passed the bound. -// */ -// animationCcw = false; -// } -// } -// } else if (!ccw && mThumbCwBound != Integer.MIN_VALUE) { -// // See block above for general comments -// int deltaCwBoundAndTouch = -// getDelta(mThumbCwBound, touchAngle, true, ccw); -// if (deltaCwBoundAndTouch <= -mCallbackThreshold / 2) { -// int deltaPreviousCbAndTouch = getDelta(mPreviousCallbackAngle, -// touchAngle, true, ccw); -// /* -// * Both of these will be negative since we got delta in -// * clockwise direction, and we want the magnitude of -// * deltaPreviousCbAndTouch to be greater than the magnitude -// * of deltaCwBoundAndTouch -// */ -// if (deltaPreviousCbAndTouch <= deltaCwBoundAndTouch) { -// touchAngle = mThumbCwBound; -// animationCcw = true; -// } -// } -// } -// if (touchAngle != oldTouchAngle) { -// // We bounded the touch angle -// deltaPrevCbAndTouch = getDelta(mPreviousCallbackAngle, touchAngle, true, ccw); -// // Animate back to the bound -// animateThumbToNewAngle = true; -// // Disallow movement now -// setMode(MODE_IGNORE_UNTIL_UP); -// } -// -// -// /* -// * Prevent it from jumping too far (this could happen if the user -// * goes through the center) -// */ -// -// if (mEnforceMaxAbsJump) { -// if (deltaPrevCbAndTouch <= -MAX_ABS_JUMP_DELTA_ANGLE) { -// deltaPrevCbAndTouch = -MAX_ABS_JUMP_DELTA_ANGLE; -// animateThumbToNewAngle = true; -// } else if (deltaPrevCbAndTouch >= MAX_ABS_JUMP_DELTA_ANGLE) { -// deltaPrevCbAndTouch = MAX_ABS_JUMP_DELTA_ANGLE; -// animateThumbToNewAngle = true; -// } -// } -// -// /* -// * We need to cover the edge case of a user grabbing the thumb, -// * going into the center of the widget, and then coming out from the -// * center to an angle that's slightly below the angle he's trying to -// * hit. If we do int division, we'll end up with one level lower -// * than the one he was going for. -// */ -// int deltaLevels = Math.round((float) deltaPrevCbAndTouch / mCallbackThreshold); -// if (deltaLevels != 0) { -// boolean canStillZoom = mCallback.onZoomRingThumbDragged( -// deltaLevels, mThumbDragStartAngle, touchAngle); -// -// if (mVibration) { -// // TODO: we're trying the haptics to see how it goes with -// // users, so we're ignoring the settings (for now) -// performHapticFeedback(HapticFeedbackConstants.ZOOM_RING_TICK, -// HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING | -// HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); -// -// } -// // Set the callback angle to the actual angle based on how many delta levels we gave -// mPreviousCallbackAngle = getValidAngle( -// mPreviousCallbackAngle + (deltaLevels * mCallbackThreshold)); -// } -// } -// -// if (DRAW_TRAIL) { -// int deltaAngle = getDelta(mThumbAngle, touchAngle, true, ccw); -// mAcculumalatedTrailAngle += Math.toDegrees(deltaAngle / (double) RADIAN_INT_MULTIPLIER); -// } -// -// if (animateThumbToNewAngle) { -// setThumbAngleAnimated(touchAngle, 0, animationCcw); -// } else { -// /* -// * Use regular ccw here because animationCcw will never have been -// * changed if animateThumbToNewAngle is false -// */ -// setThumbAngleAuto(touchAngle, true, ccw); -// } -// } private int getValidAngle(int invalidAngle) { if (invalidAngle < 0) { @@ -1008,16 +818,16 @@ public class ZoomRing extends View { mCallback.onZoomRingThumbDraggingStopped(); } - private void onZoomRingMoved(int rawX, int rawY) { + private void onZoomRingMoved(int x, int y) { if (mPreviousWidgetDragX != Integer.MIN_VALUE) { - int deltaX = rawX - mPreviousWidgetDragX; - int deltaY = rawY - mPreviousWidgetDragY; + int deltaX = x - mPreviousWidgetDragX; + int deltaY = y - mPreviousWidgetDragY; - mCallback.onZoomRingMoved(deltaX, deltaY, rawX, rawY); + mCallback.onZoomRingMoved(deltaX, deltaY); } - mPreviousWidgetDragX = rawX; - mPreviousWidgetDragY = rawY; + mPreviousWidgetDragX = x; + mPreviousWidgetDragY = y; } @Override @@ -1049,17 +859,15 @@ public class ZoomRing extends View { protected void onDraw(Canvas canvas) { super.onDraw(canvas); - if (mThumbVisible) { + if (mDrawThumb) { if (DRAW_TRAIL) { mTrail.draw(canvas); } if ((mThumbArrowsToDraw & THUMB_ARROW_PLUS) != 0) { mThumbPlusArrowDrawable.draw(canvas); - mThumbPlusDrawable.draw(canvas); } if ((mThumbArrowsToDraw & THUMB_ARROW_MINUS) != 0) { mThumbMinusArrowDrawable.draw(canvas); - mThumbMinusDrawable.draw(canvas); } mThumbDrawable.draw(canvas); } @@ -1069,28 +877,6 @@ public class ZoomRing extends View { int level = -angle * 10000 / ZoomRing.TWO_PI_INT_MULTIPLIED; mThumbPlusArrowDrawable.setLevel(level); mThumbMinusArrowDrawable.setLevel(level); - - // Assume it is a square - int halfSideLength = mThumbPlusDrawable.getIntrinsicHeight() / 2; - int unoffsetAngle = angle + mZeroAngle; - - int plusCenterX = (int) (Math.cos(1f * (unoffsetAngle - THUMB_PLUS_MINUS_OFFSET_ANGLE) - / RADIAN_INT_MULTIPLIER) * THUMB_PLUS_MINUS_DISTANCE) + mCenterX; - int plusCenterY = (int) (Math.sin(1f * (unoffsetAngle - THUMB_PLUS_MINUS_OFFSET_ANGLE) - / RADIAN_INT_MULTIPLIER) * THUMB_PLUS_MINUS_DISTANCE) * -1 + mCenterY; - mThumbPlusDrawable.setBounds(plusCenterX - halfSideLength, - plusCenterY - halfSideLength, - plusCenterX + halfSideLength, - plusCenterY + halfSideLength); - - int minusCenterX = (int) (Math.cos(1f * (unoffsetAngle + THUMB_PLUS_MINUS_OFFSET_ANGLE) - / RADIAN_INT_MULTIPLIER) * THUMB_PLUS_MINUS_DISTANCE) + mCenterX; - int minusCenterY = (int) (Math.sin(1f * (unoffsetAngle + THUMB_PLUS_MINUS_OFFSET_ANGLE) - / RADIAN_INT_MULTIPLIER) * THUMB_PLUS_MINUS_DISTANCE) * -1 + mCenterY; - mThumbMinusDrawable.setBounds(minusCenterX - halfSideLength, - minusCenterY - halfSideLength, - minusCenterX + halfSideLength, - minusCenterY + halfSideLength); } public void setThumbArrowsVisible(boolean visible) { @@ -1100,7 +886,6 @@ public class ZoomRing extends View { if (callbackAngle < mThumbCwBound - RADIAN_INT_ERROR || callbackAngle > mThumbCwBound + RADIAN_INT_ERROR) { mThumbPlusArrowDrawable.setAlpha(255); - mThumbPlusDrawable.setAlpha(255); mThumbArrowsToDraw |= THUMB_ARROW_PLUS; } else { mThumbArrowsToDraw &= ~THUMB_ARROW_PLUS; @@ -1108,7 +893,6 @@ public class ZoomRing extends View { if (callbackAngle < mThumbCcwBound - RADIAN_INT_ERROR || callbackAngle > mThumbCcwBound + RADIAN_INT_ERROR) { mThumbMinusArrowDrawable.setAlpha(255); - mThumbMinusDrawable.setAlpha(255); mThumbArrowsToDraw |= THUMB_ARROW_MINUS; } else { mThumbArrowsToDraw &= ~THUMB_ARROW_MINUS; @@ -1133,14 +917,10 @@ public class ZoomRing extends View { if (mThumbArrowsAlpha < 0) mThumbArrowsAlpha = 0; if ((mThumbArrowsToDraw & THUMB_ARROW_PLUS) != 0) { mThumbPlusArrowDrawable.setAlpha(mThumbArrowsAlpha); - mThumbPlusDrawable.setAlpha(mThumbArrowsAlpha); - invalidateDrawable(mThumbPlusDrawable); invalidateDrawable(mThumbPlusArrowDrawable); } if ((mThumbArrowsToDraw & THUMB_ARROW_MINUS) != 0) { mThumbMinusArrowDrawable.setAlpha(mThumbArrowsAlpha); - mThumbMinusDrawable.setAlpha(mThumbArrowsAlpha); - invalidateDrawable(mThumbMinusDrawable); invalidateDrawable(mThumbMinusArrowDrawable); } @@ -1161,7 +941,7 @@ public class ZoomRing extends View { void onZoomRingSetMovableHintVisible(boolean visible); void onZoomRingMovingStarted(); - boolean onZoomRingMoved(int deltaX, int deltaY, int rawX, int rawY); + boolean onZoomRingMoved(int deltaX, int deltaY); void onZoomRingMovingStopped(); void onZoomRingThumbDraggingStarted(); diff --git a/core/java/android/widget/ZoomRingController.java b/core/java/android/widget/ZoomRingController.java index 19f66a0..2e97fda 100644 --- a/core/java/android/widget/ZoomRingController.java +++ b/core/java/android/widget/ZoomRingController.java @@ -33,7 +33,6 @@ import android.provider.Settings; import android.util.Log; import android.view.Gravity; import android.view.KeyEvent; -import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; @@ -48,36 +47,30 @@ import android.view.animation.DecelerateInterpolator; /** * TODO: Docs - * + * * If you are using this with a custom View, please call * {@link #setVisible(boolean) setVisible(false)} from the * {@link View#onDetachedFromWindow}. - * + * * @hide */ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, View.OnTouchListener, View.OnKeyListener { - + private static final int ZOOM_RING_RADIUS_INSET = 24; private static final int ZOOM_RING_RECENTERING_DURATION = 500; private static final String TAG = "ZoomRing"; - public static final boolean USE_OLD_ZOOM = false; - static int getZoomType(Context context) { - return Settings.System.getInt(context.getContentResolver(), "zoom", 1); - } + public static final boolean USE_OLD_ZOOM = false; public static boolean useOldZoom(Context context) { - return getZoomType(context) == 0; + return Settings.System.getInt(context.getContentResolver(), "zoom", 1) == 0; } - private static boolean useThisZoom(Context context) { - return getZoomType(context) == 1; - } - + private static final int ZOOM_CONTROLS_TIMEOUT = (int) ViewConfiguration.getZoomControlsTimeout(); - + // TODO: move these to ViewConfiguration or re-use existing ones // TODO: scale px values based on latest from ViewConfiguration private static final int SECOND_TAP_TIMEOUT = 500; @@ -87,12 +80,12 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, private static final int MAX_INITIATE_PAN_GAP = 10; // TODO view config private static final int INITIATE_PAN_DELAY = 300; - + private static final String SETTING_NAME_SHOWN_TOAST = "shown_zoom_ring_toast"; - + private Context mContext; private WindowManager mWindowManager; - + /** * The view that is being zoomed by this zoom ring. */ @@ -118,15 +111,15 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, /** * The {@link #mTouchTargetView}'s location in window, set on touch down. */ - private int[] mTouchTargetLocationInWindow = new int[2]; + private int[] mTouchTargetLocationInWindow = new int[2]; /** * If the zoom ring is dismissed but the user is still in a touch * interaction, we set this to true. This will ignore all touch events until * up/cancel, and then set the owner's touch listener to null. */ private boolean mReleaseTouchListenerOnUp; - - + + /* * Tap-drag is an interaction where the user first taps and then (quickly) * does the clockwise or counter-clockwise drag. In reality, this is: (down, @@ -139,40 +132,30 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, */ private int mTapDragStartX; private int mTapDragStartY; - + private static final int TOUCH_MODE_IDLE = 0; private static final int TOUCH_MODE_WAITING_FOR_SECOND_TAP = 1; private static final int TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT = 2; private static final int TOUCH_MODE_FORWARDING_FOR_TAP_DRAG = 3; private int mTouchMode; - + private boolean mIsZoomRingVisible; - + private ZoomRing mZoomRing; private int mZoomRingWidth; private int mZoomRingHeight; - + /** Invokes panning of owner view if the zoom ring is touching an edge. */ private Panner mPanner; private long mTouchingEdgeStartTime; private boolean mPanningEnabledForThisInteraction; - - /** - * When the finger moves the zoom ring to an edge, this is the horizontal - * accumulator for how much the finger has moved off of its original touch - * point on the zoom ring (OOB = out-of-bounds). If < 0, the finger has - * moved that many px to the left of its original touch point on the ring. - */ - private int mMovingZoomRingOobX; - /** Vertical accumulator, see {@link #mMovingZoomRingOobX} */ - private int mMovingZoomRingOobY; - + private ImageView mPanningArrows; private Animation mPanningArrowsEnterAnimation; private Animation mPanningArrowsExitAnimation; - + private Rect mTempRect = new Rect(); - + private OnZoomListener mCallback; private ViewConfiguration mViewConfig; @@ -188,7 +171,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, * for the container's layout params. */ private int mCenteredContainerY = Integer.MIN_VALUE; - + /** * Scroller used to re-center the zoom ring if the user had dragged it to a * corner and then double-taps any point on the owner view (the owner view @@ -198,7 +181,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, * The (x,y) of the scroller is the (x,y) of the container's layout params. */ private Scroller mScroller; - + /** * When showing the zoom ring, we add the view as a new window. However, * there is logic that needs to know the size of the zoom ring which is @@ -206,7 +189,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, * the UI thread so it will be exceuted AFTER the layout. This is the logic. */ private Runnable mPostedVisibleInitializer; - + /** * Only touch from the main thread. */ @@ -216,29 +199,23 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, private IntentFilter mConfigurationChangedFilter = new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED); - + private BroadcastReceiver mConfigurationChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (!mIsZoomRingVisible) return; - + mHandler.removeMessages(MSG_POST_CONFIGURATION_CHANGED); mHandler.sendEmptyMessage(MSG_POST_CONFIGURATION_CHANGED); } }; - + /** Keeps the scroller going (or starts it). */ private static final int MSG_SCROLLER_TICK = 1; /** When configuration changes, this is called after the UI thread is idle. */ private static final int MSG_POST_CONFIGURATION_CHANGED = 2; /** Used to delay the zoom ring dismissal. */ private static final int MSG_DISMISS_ZOOM_RING = 3; - - /** - * If setVisible(true) is called and the owner view's window token is null, - * we delay the setVisible(true) call until it is not null. - */ - private static final int MSG_POST_SET_VISIBLE = 4; private Handler mHandler = new Handler() { @Override @@ -247,36 +224,26 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, case MSG_SCROLLER_TICK: onScrollerTick(); break; - + case MSG_POST_CONFIGURATION_CHANGED: onPostConfigurationChanged(); break; - + case MSG_DISMISS_ZOOM_RING: setVisible(false); break; - - case MSG_POST_SET_VISIBLE: - if (mOwnerView.getWindowToken() == null) { - // Doh, it is still null, throw an exception - throw new IllegalArgumentException( - "Cannot make the zoom ring visible if the owner view is " + - "not attached to a window."); - } - setVisible(true); - break; } - - } + + } }; - + public ZoomRingController(Context context, View ownerView) { mContext = context; mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); mPanner = new Panner(); mOwnerView = ownerView; - + mZoomRing = new ZoomRing(context); mZoomRing.setId(com.android.internal.R.id.zoomControls); mZoomRing.setLayoutParams(new FrameLayout.LayoutParams( @@ -284,7 +251,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER)); mZoomRing.setCallback(this); - + createPanningArrows(); mContainerLayoutParams = new LayoutParams(); @@ -302,12 +269,12 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, mContainer = new FrameLayout(context); mContainer.setLayoutParams(mContainerLayoutParams); mContainer.setMeasureAllChildren(true); - + mContainer.addView(mZoomRing); mContainer.addView(mPanningArrows); - + mScroller = new Scroller(context, new DecelerateInterpolator()); - + mViewConfig = ViewConfiguration.get(context); } @@ -320,7 +287,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER)); mPanningArrows.setVisibility(View.INVISIBLE); - + mPanningArrowsEnterAnimation = AnimationUtils.loadAnimation(mContext, com.android.internal.R.anim.fade_in); mPanningArrowsExitAnimation = AnimationUtils.loadAnimation(mContext, @@ -332,7 +299,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, * get a callback. Once there is a callback, the accumulator resets. For * example, if you set this to PI/6, it will give a callback every time the * user moves PI/6 amount on the ring. - * + * * @param callbackThreshold The angle for the callback threshold, in radians */ public void setZoomCallbackThreshold(float callbackThreshold) { @@ -341,7 +308,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, /** * Sets a drawable for the zoom ring track. - * + * * @param drawable The drawable to use for the track. * @hide Need a better way of doing this, but this one-off for browser so it * can have its final look for the usability study @@ -349,11 +316,11 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, public void setZoomRingTrack(int drawable) { mZoomRing.setBackgroundResource(drawable); } - + public void setCallback(OnZoomListener callback) { mCallback = callback; } - + public void setThumbAngle(float angle) { mZoomRing.setThumbAngle((int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER)); } @@ -361,21 +328,13 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, public void setThumbAngleAnimated(float angle) { mZoomRing.setThumbAngleAnimated((int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER), 0); } - + public void setResetThumbAutomatically(boolean resetThumbAutomatically) { mZoomRing.setResetThumbAutomatically(resetThumbAutomatically); } - - public void setVibration(boolean vibrate) { - mZoomRing.setVibration(vibrate); - } - - public void setThumbVisible(boolean thumbVisible) { - mZoomRing.setThumbVisible(thumbVisible); - } public void setThumbClockwiseBound(float angle) { - mZoomRing.setThumbClockwiseBound(angle >= 0 ? + mZoomRing.setThumbClockwiseBound(angle >= 0 ? (int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER) : Integer.MIN_VALUE); } @@ -392,26 +351,14 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, public void setVisible(boolean visible) { - if (!useThisZoom(mContext)) return; + if (useOldZoom(mContext)) return; if (visible) { - if (mOwnerView.getWindowToken() == null) { - /* - * We need a window token to show ourselves, maybe the owner's - * window hasn't been created yet but it will have been by the - * time the looper is idle, so post the setVisible(true) call. - */ - if (!mHandler.hasMessages(MSG_POST_SET_VISIBLE)) { - mHandler.sendEmptyMessage(MSG_POST_SET_VISIBLE); - } - return; - } - dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT); } else { mPanner.stop(); } - + if (mIsZoomRingVisible == visible) { return; } @@ -421,40 +368,40 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, if (mContainerLayoutParams.token == null) { mContainerLayoutParams.token = mOwnerView.getWindowToken(); } - + mWindowManager.addView(mContainer, mContainerLayoutParams); - + if (mPostedVisibleInitializer == null) { mPostedVisibleInitializer = new Runnable() { public void run() { refreshPositioningVariables(); resetZoomRing(); - + // TODO: remove this 'update' and just center zoom ring before the // 'add', but need to make sure we have the width and height (which // probably can only be retrieved after it's measured, which happens // after it's added). mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams); - + if (mCallback != null) { mCallback.onVisibilityChanged(true); } } - }; + }; } - + mPanningArrows.setAnimation(null); - + mHandler.post(mPostedVisibleInitializer); - + // Handle configuration changes when visible mContext.registerReceiver(mConfigurationChangedReceiver, mConfigurationChangedFilter); - + // Steal key/touches events from the owner mOwnerView.setOnKeyListener(this); mOwnerView.setOnTouchListener(this); mReleaseTouchListenerOnUp = false; - + } else { // Don't want to steal any more keys/touches mOwnerView.setOnKeyListener(null); @@ -468,45 +415,45 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, // No longer care about configuration changes mContext.unregisterReceiver(mConfigurationChangedReceiver); - + mWindowManager.removeView(mContainer); mHandler.removeCallbacks(mPostedVisibleInitializer); - + if (mCallback != null) { mCallback.onVisibilityChanged(false); } } - + } - + /** * TODO: docs - * + * * Notes: * - Touch dispatching is different. Only direct children who are clickable are eligble for touch events. * - Please ensure you set your View to INVISIBLE not GONE when hiding it. - * + * * @return */ public FrameLayout getContainer() { return mContainer; } - + public int getZoomRingId() { return mZoomRing.getId(); } - + private void dismissZoomRingDelayed(int delay) { mHandler.removeMessages(MSG_DISMISS_ZOOM_RING); mHandler.sendEmptyMessageDelayed(MSG_DISMISS_ZOOM_RING, delay); } - + private void resetZoomRing() { mScroller.abortAnimation(); - + mContainerLayoutParams.x = mCenteredContainerX; mContainerLayoutParams.y = mCenteredContainerY; - + // Reset the thumb mZoomRing.resetThumbAngle(); } @@ -514,15 +461,13 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, /** * Should be called by the client for each event belonging to the second tap * (the down, move, up, and cancel events). - * + * * @param event The event belonging to the second tap. * @return Whether the event was consumed. */ public boolean handleDoubleTapEvent(MotionEvent event) { - if (!useThisZoom(mContext)) return false; - int action = event.getAction(); - + // TODO: make sure this works well with the // ownerView.setOnTouchListener(this) instead of window receiving // touches @@ -530,19 +475,19 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, mTouchMode = TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT; int x = (int) event.getX(); int y = (int) event.getY(); - + refreshPositioningVariables(); setVisible(true); centerPoint(x, y); - ensureZoomRingIsCentered(); - + ensureZoomRingIsCentered(); + // Tap drag mode stuff mTapDragStartX = x; mTapDragStartY = y; } else if (action == MotionEvent.ACTION_CANCEL) { mTouchMode = TOUCH_MODE_IDLE; - + } else { // action is move or up switch (mTouchMode) { case TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT: { @@ -558,29 +503,29 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, setTouchTargetView(mZoomRing); } return true; - + case MotionEvent.ACTION_UP: mTouchMode = TOUCH_MODE_IDLE; break; } break; } - + case TOUCH_MODE_FORWARDING_FOR_TAP_DRAG: { switch (action) { case MotionEvent.ACTION_MOVE: giveTouchToZoomRing(event); return true; - + case MotionEvent.ACTION_UP: mTouchMode = TOUCH_MODE_IDLE; - + /* * This is a power-user feature that only shows the * zoom while the user is performing the tap-drag. * That means once it is released, the zoom ring * should disappear. - */ + */ mZoomRing.setTapDragMode(false, (int) event.getX(), (int) event.getY()); dismissZoomRingDelayed(0); break; @@ -589,13 +534,13 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, } } } - + return true; } - + private void ensureZoomRingIsCentered() { LayoutParams lp = mContainerLayoutParams; - + if (lp.x != mCenteredContainerX || lp.y != mCenteredContainerY) { int width = mContainer.getWidth(); int height = mContainer.getHeight(); @@ -604,21 +549,21 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, mHandler.sendEmptyMessage(MSG_SCROLLER_TICK); } } - + private void refreshPositioningVariables() { mZoomRingWidth = mZoomRing.getWidth(); mZoomRingHeight = mZoomRing.getHeight(); - + // Calculate the owner view's bounds mOwnerView.getGlobalVisibleRect(mOwnerViewBounds); - + // Get the center Gravity.apply(Gravity.CENTER, mContainer.getWidth(), mContainer.getHeight(), mOwnerViewBounds, mTempRect); mCenteredContainerX = mTempRect.left; mCenteredContainerY = mTempRect.top; } - + /** * Centers the point (in owner view's coordinates). */ @@ -627,7 +572,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, mCallback.onCenter(x, y); } } - + private void giveTouchToZoomRing(MotionEvent event) { int rawX = (int) event.getRawX(); int rawY = (int) event.getRawY(); @@ -635,11 +580,11 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, int y = rawY - mContainerLayoutParams.y - mZoomRing.getTop(); mZoomRing.handleTouch(event.getAction(), event.getEventTime(), x, y, rawX, rawY); } - + public void onZoomRingSetMovableHintVisible(boolean visible) { - setPanningArrowsVisible(visible); + setPanningArrowsVisible(visible); } - + public void onUserInteractionStarted() { mHandler.removeMessages(MSG_DISMISS_ZOOM_RING); } @@ -651,62 +596,24 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, public void onZoomRingMovingStarted() { mScroller.abortAnimation(); mTouchingEdgeStartTime = 0; - mMovingZoomRingOobX = 0; - mMovingZoomRingOobY = 0; if (mCallback != null) { mCallback.onBeginPan(); } } - + private void setPanningArrowsVisible(boolean visible) { mPanningArrows.startAnimation(visible ? mPanningArrowsEnterAnimation : mPanningArrowsExitAnimation); mPanningArrows.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); } - - public boolean onZoomRingMoved(int deltaX, int deltaY, int rawX, int rawY) { - - if (mMovingZoomRingOobX != 0) { - /* - * The finger has moved off the point where it originally touched - * the zidget. - */ - boolean wasOobLeft = mMovingZoomRingOobX < 0; - mMovingZoomRingOobX += deltaX; - if ((wasOobLeft && mMovingZoomRingOobX > 0) || - (!wasOobLeft && mMovingZoomRingOobX < 0)) { - /* - * Woot, the finger is back on the original point. Infact, it - * went PAST its original point, so take the amount it passed - * and use that as the delta to move the zoom ring. - */ - deltaX = mMovingZoomRingOobX; - // No longer out-of-bounds, reset - mMovingZoomRingOobX = 0; - } else { - // The finger is still not back, eat this movement - deltaX = 0; - } - } - - if (mMovingZoomRingOobY != 0) { - // See above for comments - boolean wasOobUp = mMovingZoomRingOobY < 0; - mMovingZoomRingOobY += deltaY; - if ((wasOobUp && mMovingZoomRingOobY > 0) || (!wasOobUp && mMovingZoomRingOobY < 0)) { - deltaY = mMovingZoomRingOobY; - mMovingZoomRingOobY = 0; - } else { - deltaY = 0; - } - } - + + public boolean onZoomRingMoved(int deltaX, int deltaY) { WindowManager.LayoutParams lp = mContainerLayoutParams; Rect ownerBounds = mOwnerViewBounds; - + int zoomRingLeft = mZoomRing.getLeft(); int zoomRingTop = mZoomRing.getTop(); - + int newX = lp.x + deltaX; int newZoomRingX = newX + zoomRingLeft; newZoomRingX = (newZoomRingX <= ownerBounds.left) ? ownerBounds.left : @@ -720,26 +627,19 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, (newZoomRingY + mZoomRingHeight > ownerBounds.bottom) ? ownerBounds.bottom - mZoomRingHeight : newZoomRingY; lp.y = newZoomRingY - zoomRingTop; - + mWindowManager.updateViewLayout(mContainer, lp); - + // Check for pan boolean horizontalPanning = true; int leftGap = newZoomRingX - ownerBounds.left; if (leftGap < MAX_PAN_GAP) { - if (leftGap == 0 && deltaX != 0 && mMovingZoomRingOobX == 0) { - // Future moves in this direction should be accumulated in mMovingZoomRingOobX - mMovingZoomRingOobX = deltaX / Math.abs(deltaX); - } if (shouldPan(leftGap)) { mPanner.setHorizontalStrength(-getStrengthFromGap(leftGap)); } } else { int rightGap = ownerBounds.right - (lp.x + mZoomRingWidth + zoomRingLeft); if (rightGap < MAX_PAN_GAP) { - if (rightGap == 0 && deltaX != 0 && mMovingZoomRingOobX == 0) { - mMovingZoomRingOobX = deltaX / Math.abs(deltaX); - } if (shouldPan(rightGap)) { mPanner.setHorizontalStrength(getStrengthFromGap(rightGap)); } @@ -748,21 +648,15 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, horizontalPanning = false; } } - + int topGap = newZoomRingY - ownerBounds.top; if (topGap < MAX_PAN_GAP) { - if (topGap == 0 && deltaY != 0 && mMovingZoomRingOobY == 0) { - mMovingZoomRingOobY = deltaY / Math.abs(deltaY); - } if (shouldPan(topGap)) { mPanner.setVerticalStrength(-getStrengthFromGap(topGap)); } } else { int bottomGap = ownerBounds.bottom - (lp.y + mZoomRingHeight + zoomRingTop); if (bottomGap < MAX_PAN_GAP) { - if (bottomGap == 0 && deltaY != 0 && mMovingZoomRingOobY == 0) { - mMovingZoomRingOobY = deltaY / Math.abs(deltaY); - } if (shouldPan(bottomGap)) { mPanner.setVerticalStrength(getStrengthFromGap(bottomGap)); } @@ -776,13 +670,13 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, } } } - + return true; } - + private boolean shouldPan(int gap) { if (mPanningEnabledForThisInteraction) return true; - + if (gap < MAX_INITIATE_PAN_GAP) { long time = SystemClock.elapsedRealtime(); if (mTouchingEdgeStartTime != 0 && @@ -799,7 +693,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, } return false; } - + public void onZoomRingMovingStopped() { mPanner.stop(); setPanningArrowsVisible(false); @@ -807,18 +701,18 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, mCallback.onEndPan(); } } - + private int getStrengthFromGap(int gap) { return gap > MAX_PAN_GAP ? 0 : (MAX_PAN_GAP - gap) * 100 / MAX_PAN_GAP; } - + public void onZoomRingThumbDraggingStarted() { if (mCallback != null) { mCallback.onBeginDrag(); } } - + public boolean onZoomRingThumbDragged(int numLevels, int startAngle, int curAngle) { if (mCallback != null) { int deltaZoomLevel = -numLevels; @@ -826,17 +720,17 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, mZoomRingWidth / 2; int globalZoomCenterY = mContainerLayoutParams.y + mZoomRing.getTop() + mZoomRingHeight / 2; - + return mCallback.onDragZoom(deltaZoomLevel, globalZoomCenterX - mOwnerViewBounds.left, globalZoomCenterY - mOwnerViewBounds.top, (float) startAngle / ZoomRing.RADIAN_INT_MULTIPLIER, (float) curAngle / ZoomRing.RADIAN_INT_MULTIPLIER); } - + return false; } - + public void onZoomRingThumbDraggingStopped() { if (mCallback != null) { mCallback.onEndDrag(); @@ -846,35 +740,35 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, public void onZoomRingDismissed(boolean dismissImmediately) { if (dismissImmediately) { mHandler.removeMessages(MSG_DISMISS_ZOOM_RING); - setVisible(false); + setVisible(false); } else { dismissZoomRingDelayed(ZOOM_RING_DISMISS_DELAY); } } - + public void onRingDown(int tickAngle, int touchAngle) { } - + public boolean onTouch(View v, MotionEvent event) { if (sTutorialDialog != null && sTutorialDialog.isShowing() && SystemClock.elapsedRealtime() - sTutorialShowTime >= TUTORIAL_MIN_DISPLAY_TIME) { finishZoomTutorial(); } - + int action = event.getAction(); if (mReleaseTouchListenerOnUp) { - // The ring was dismissed but we need to throw away all events until the up + // The ring was dismissed but we need to throw away all events until the up if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { mOwnerView.setOnTouchListener(null); setTouchTargetView(null); mReleaseTouchListenerOnUp = false; } - + // Eat this event return true; } - + View targetView = mTouchTargetView; switch (action) { @@ -882,7 +776,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, targetView = getViewForTouch((int) event.getRawX(), (int) event.getRawY()); setTouchTargetView(targetView); break; - + case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: setTouchTargetView(null); @@ -893,7 +787,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, // The upperleft corner of the target view in raw coordinates int targetViewRawX = mContainerLayoutParams.x + mTouchTargetLocationInWindow[0]; int targetViewRawY = mContainerLayoutParams.y + mTouchTargetLocationInWindow[1]; - + MotionEvent containerEvent = MotionEvent.obtain(event); // Convert the motion event into the target view's coordinates (from // owner view's coordinates) @@ -902,32 +796,32 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, boolean retValue = targetView.dispatchTouchEvent(containerEvent); containerEvent.recycle(); return retValue; - + } else { if (action == MotionEvent.ACTION_DOWN) { dismissZoomRingDelayed(ZOOM_RING_DISMISS_DELAY); } - + return false; } } - + private void setTouchTargetView(View view) { mTouchTargetView = view; if (view != null) { view.getLocationInWindow(mTouchTargetLocationInWindow); } } - + /** * Returns the View that should receive a touch at the given coordinates. - * + * * @param rawX The raw X. * @param rawY The raw Y. * @return The view that should receive the touches, or null if there is not one. */ private View getViewForTouch(int rawX, int rawY) { - // Check to see if it is touching the ring + // Check to see if it is touching the ring int containerCenterX = mContainerLayoutParams.x + mContainer.getWidth() / 2; int containerCenterY = mContainerLayoutParams.y + mContainer.getHeight() / 2; int distanceFromCenterX = rawX - containerCenterX; @@ -938,7 +832,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, zoomRingRadius * zoomRingRadius) { return mZoomRing; } - + // Check to see if it is touching any other clickable View. // Reverse order so the child drawn on top gets first dibs. int containerCoordsX = rawX - mContainerLayoutParams.x; @@ -950,13 +844,13 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, !child.isClickable()) { continue; } - + child.getHitRect(frame); if (frame.contains(containerCoordsX, containerCoordsY)) { return child; } } - + return null; } @@ -967,34 +861,34 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, case KeyEvent.KEYCODE_DPAD_RIGHT: // Eat these return true; - + case KeyEvent.KEYCODE_DPAD_UP: case KeyEvent.KEYCODE_DPAD_DOWN: // Keep the zoom alive a little longer dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT); // They started zooming, hide the thumb arrows mZoomRing.setThumbArrowsVisible(false); - + if (mCallback != null && event.getAction() == KeyEvent.ACTION_DOWN) { mCallback.onSimpleZoom(keyCode == KeyEvent.KEYCODE_DPAD_UP); } - + return true; } - + return false; } private void onScrollerTick() { if (!mScroller.computeScrollOffset() || !mIsZoomRingVisible) return; - + mContainerLayoutParams.x = mScroller.getCurrX(); mContainerLayoutParams.y = mScroller.getCurrY(); mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams); mHandler.sendEmptyMessage(MSG_SCROLLER_TICK); } - + private void onPostConfigurationChanged() { dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT); refreshPositioningVariables(); @@ -1014,7 +908,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, * before. Furthermore, if the application does not have privilege to write * to the system settings, it will store this bit locally in a shared * preference. - * + * * @hide This should only be used by our main apps--browser, maps, and * gallery */ @@ -1023,65 +917,53 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, if (Settings.System.getInt(cr, SETTING_NAME_SHOWN_TOAST, 0) == 1) { return; } - + SharedPreferences sp = context.getSharedPreferences("_zoom", Context.MODE_PRIVATE); if (sp.getInt(SETTING_NAME_SHOWN_TOAST, 0) == 1) { return; } - + if (sTutorialDialog != null && sTutorialDialog.isShowing()) { sTutorialDialog.dismiss(); } - - LayoutInflater layoutInflater = - (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - TextView textView = (TextView) layoutInflater.inflate( - com.android.internal.R.layout.alert_dialog_simple_text, null) - .findViewById(android.R.id.text1); - textView.setText(com.android.internal.R.string.tutorial_double_tap_to_zoom_message_short); sTutorialDialog = new AlertDialog.Builder(context) - .setView(textView) + .setMessage( + com.android.internal.R.string.tutorial_double_tap_to_zoom_message_short) .setIcon(0) .create(); - + Window window = sTutorialDialog.getWindow(); window.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM); - window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND | + window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND | WindowManager.LayoutParams.FLAG_BLUR_BEHIND); window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); - + sTutorialDialog.show(); sTutorialShowTime = SystemClock.elapsedRealtime(); } - - public static void finishZoomTutorial(Context context, boolean userNotified) { + + public void finishZoomTutorial() { if (sTutorialDialog == null) return; - + sTutorialDialog.dismiss(); sTutorialDialog = null; - + // Record that they have seen the tutorial - if (userNotified) { - try { - Settings.System.putInt(context.getContentResolver(), SETTING_NAME_SHOWN_TOAST, 1); - } catch (SecurityException e) { - /* - * The app does not have permission to clear this global flag, make - * sure the user does not see the message when he comes back to this - * same app at least. - */ - SharedPreferences sp = context.getSharedPreferences("_zoom", Context.MODE_PRIVATE); - sp.edit().putInt(SETTING_NAME_SHOWN_TOAST, 1).commit(); - } + try { + Settings.System.putInt(mContext.getContentResolver(), SETTING_NAME_SHOWN_TOAST, 1); + } catch (SecurityException e) { + /* + * The app does not have permission to clear this global flag, make + * sure the user does not see the message when he comes back to this + * same app at least. + */ + SharedPreferences sp = mContext.getSharedPreferences("_zoom", Context.MODE_PRIVATE); + sp.edit().putInt(SETTING_NAME_SHOWN_TOAST, 1).commit(); } } - - public void finishZoomTutorial() { - finishZoomTutorial(mContext, true); - } - + public void setPannerStartVelocity(float startVelocity) { mPanner.mStartVelocity = startVelocity; } @@ -1101,27 +983,27 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, private class Panner implements Runnable { private static final int RUN_DELAY = 15; private static final float STOP_SLOWDOWN = 0.8f; - + private final Handler mUiHandler = new Handler(); - + private int mVerticalStrength; private int mHorizontalStrength; private boolean mStopping; - + /** The time this current pan started. */ private long mStartTime; - + /** The time of the last callback to pan the map/browser/etc. */ private long mPreviousCallbackTime; - + // TODO Adjust to be DPI safe private float mStartVelocity = 135; private float mAcceleration = 160; private float mMaxVelocity = 1000; private int mStartAcceleratingDuration = 700; private float mVelocity; - + /** -100 (full left) to 0 (none) to 100 (full right) */ public void setHorizontalStrength(int horizontalStrength) { if (mHorizontalStrength == 0 && mVerticalStrength == 0 && horizontalStrength != 0) { @@ -1129,7 +1011,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, } else if (mVerticalStrength == 0 && horizontalStrength == 0) { stop(); } - + mHorizontalStrength = horizontalStrength; mStopping = false; } @@ -1141,11 +1023,11 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, } else if (mHorizontalStrength == 0 && verticalStrength == 0) { stop(); } - - mVerticalStrength = verticalStrength; + + mVerticalStrength = verticalStrength; mStopping = false; } - + private void start() { mUiHandler.post(this); mPreviousCallbackTime = 0; @@ -1155,37 +1037,37 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, public void stop() { mStopping = true; } - + public void run() { if (mStopping) { mHorizontalStrength *= STOP_SLOWDOWN; mVerticalStrength *= STOP_SLOWDOWN; } - + if (mHorizontalStrength == 0 && mVerticalStrength == 0) { return; } - + boolean firstRun = mPreviousCallbackTime == 0; long curTime = SystemClock.elapsedRealtime(); int panAmount = getPanAmount(mPreviousCallbackTime, curTime); mPreviousCallbackTime = curTime; - + if (firstRun) { mStartTime = curTime; mVelocity = mStartVelocity; } else { int panX = panAmount * mHorizontalStrength / 100; int panY = panAmount * mVerticalStrength / 100; - + if (mCallback != null) { mCallback.onPan(panX, panY); } } - + mUiHandler.postDelayed(this, RUN_DELAY); } - + private int getPanAmount(long previousTime, long currentTime) { if (mVelocity > mMaxVelocity) { mVelocity = mMaxVelocity; @@ -1195,12 +1077,14 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, mVelocity += (currentTime - previousTime) * mAcceleration / 1000; } } - + return (int) ((currentTime - previousTime) * mVelocity) / 1000; } } + + public interface OnZoomListener { void onBeginDrag(); boolean onDragZoom(int deltaZoomLevel, int centerX, int centerY, float startAngle, diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index edda1d9..b502a6c 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -28,8 +28,6 @@ interface IBatteryStats { void noteStopGps(int uid); void noteScreenOn(); void noteScreenOff(); - void notePhoneOn(); - void notePhoneOff(); void setOnBattery(boolean onBattery); long getAwakeTimeBattery(); long getAwakeTimePlugged(); diff --git a/core/java/com/android/internal/gadget/IGadgetService.aidl b/core/java/com/android/internal/gadget/IGadgetService.aidl index 9c66b95..a22f3f3 100644 --- a/core/java/com/android/internal/gadget/IGadgetService.aidl +++ b/core/java/com/android/internal/gadget/IGadgetService.aidl @@ -44,7 +44,6 @@ interface IGadgetService { List<GadgetProviderInfo> getInstalledProviders(); GadgetProviderInfo getGadgetInfo(int gadgetId); void bindGadgetId(int gadgetId, in ComponentName provider); - int[] getGadgetIds(in ComponentName provider); } diff --git a/core/java/com/android/internal/logging/AndroidConfig.java b/core/java/com/android/internal/logging/AndroidConfig.java index f8002c6..d8be889 100644 --- a/core/java/com/android/internal/logging/AndroidConfig.java +++ b/core/java/com/android/internal/logging/AndroidConfig.java @@ -34,14 +34,11 @@ public class AndroidConfig { super(); try { - Logger rootLogger = Logger.getLogger(""); - rootLogger.addHandler(new AndroidHandler()); - rootLogger.setLevel(Level.INFO); - - // Turn down logging in Apache libraries. - Logger.getLogger("org.apache").setLevel(Level.WARNING); + Logger.global.addHandler(new AndroidHandler()); + Logger.global.setLevel(Level.ALL); } catch (Exception ex) { ex.printStackTrace(); } - } + } + } diff --git a/core/java/com/android/internal/logging/AndroidHandler.java b/core/java/com/android/internal/logging/AndroidHandler.java index d9fcf60..a6a4c64 100644 --- a/core/java/com/android/internal/logging/AndroidHandler.java +++ b/core/java/com/android/internal/logging/AndroidHandler.java @@ -18,14 +18,14 @@ package com.android.internal.logging; import android.util.Log; -import java.util.logging.*; -import java.util.Date; -import java.text.MessageFormat; -import java.io.PrintWriter; -import java.io.StringWriter; +import java.util.logging.Formatter; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.SimpleFormatter; /** - * Implements a {@link java.util.logging.Logger} handler that writes to the Android log. The + * Implements a {@link java.util.Logger} handler that writes to the Android log. The * implementation is rather straightforward. The name of the logger serves as * the log tag. Only the log levels need to be converted appropriately. For * this purpose, the following mapping is being used: @@ -81,24 +81,8 @@ public class AndroidHandler extends Handler { /** * Holds the formatter for all Android log handlers. */ - private static final Formatter THE_FORMATTER = new Formatter() { - @Override - public String format(LogRecord r) { - Throwable thrown = r.getThrown(); - if (thrown != null) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - sw.write(r.getMessage()); - sw.write("\n"); - thrown.printStackTrace(pw); - pw.flush(); - return sw.toString(); - } else { - return r.getMessage(); - } - } - }; - + private static final Formatter THE_FORMATTER = new SimpleFormatter(); + /** * Constructs a new instance of the Android log handler. */ @@ -122,40 +106,27 @@ public class AndroidHandler extends Handler { int level = getAndroidLevel(record.getLevel()); String tag = record.getLoggerName(); - if (tag == null) { - // Anonymous logger. - tag = "null"; - } else { - // Tags must be <= 23 characters. - int length = tag.length(); - if (length > 23) { - // Most loggers use the full class name. Try dropping the - // package. - int lastPeriod = tag.lastIndexOf("."); - if (length - lastPeriod - 1 <= 23) { - tag = tag.substring(lastPeriod + 1); - } else { - // Use last 23 chars. - tag = tag.substring(tag.length() - 23); - } - } - } - if (!Log.isLoggable(tag, level)) { return; } - - String message = getFormatter().format(record); - Log.println(level, tag, message); + + String msg; + try { + msg = getFormatter().format(record); + } catch (RuntimeException e) { + Log.e("AndroidHandler", "Error formatting log record", e); + msg = record.getMessage(); + } + Log.println(level, tag, msg); } catch (RuntimeException e) { - Log.e("AndroidHandler", "Error logging message.", e); + Log.e("AndroidHandler", "Error publishing log record", e); } } /** - * Converts a {@link java.util.logging.Logger} logging level into an Android one. + * Converts a {@link java.util.Logger} logging level into an Android one. * - * @param level The {@link java.util.logging.Logger} logging level. + * @param level The {@link java.util.Logger} logging level. * * @return The resulting Android logging level. */ diff --git a/core/java/com/android/internal/net/DbSSLSessionCache.java b/core/java/com/android/internal/net/DbSSLSessionCache.java deleted file mode 100644 index 06e4ca8..0000000 --- a/core/java/com/android/internal/net/DbSSLSessionCache.java +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright 2009 The Android Open Source Project - -package com.android.internal.net; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.SQLException; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.util.Log; - -import org.apache.commons.codec.binary.Base64; -import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache; - -import java.util.HashMap; -import java.util.Map; - -import javax.net.ssl.SSLSession; - -/** - * Hook into harmony SSL cache to persist the SSL sessions. - * - * Current implementation is suitable for saving a small number of hosts - - * like google services. It can be extended with expiration and more features - * to support more hosts. - * - * {@hide} - */ -public class DbSSLSessionCache implements SSLClientSessionCache { - private static final String TAG = "DbSSLSessionCache"; - - /** - * Table where sessions are stored. - */ - public static final String SSL_CACHE_TABLE = "ssl_sessions"; - - private static final String SSL_CACHE_ID = "_id"; - - /** - * Key is host:port - port is not optional. - */ - private static final String SSL_CACHE_HOSTPORT = "hostport"; - - /** - * Base64-encoded DER value of the session. - */ - private static final String SSL_CACHE_SESSION = "session"; - - /** - * Time when the record was added - should be close to the time - * of the initial session negotiation. - */ - private static final String SSL_CACHE_TIME_SEC = "time_sec"; - - public static final String DATABASE_NAME = "ssl_sessions.db"; - - public static final int DATABASE_VERSION = 2; - - /** public for testing - */ - public static final int SSL_CACHE_ID_COL = 0; - public static final int SSL_CACHE_HOSTPORT_COL = 1; - public static final int SSL_CACHE_SESSION_COL = 2; - public static final int SSL_CACHE_TIME_SEC_COL = 3; - - public static final int MAX_CACHE_SIZE = 256; - - private final Map<String, byte[]> mExternalCache = - new HashMap<String, byte[]>(); - - - private DatabaseHelper mDatabaseHelper; - - private boolean mNeedsCacheLoad = true; - - public static final String[] PROJECTION = new String[] { - SSL_CACHE_ID, - SSL_CACHE_HOSTPORT, - SSL_CACHE_SESSION, - SSL_CACHE_TIME_SEC - }; - - /** - * Create a SslSessionCache instance, using the specified context to - * initialize the database. - * - * This constructor will use the default database - created for the application - * context. - * - * @param activityContext - */ - public DbSSLSessionCache(Context activityContext) { - Context appContext = activityContext.getApplicationContext(); - mDatabaseHelper = new DatabaseHelper(appContext); - } - - /** - * Create a SslSessionCache that uses a specific database. - * - * - * @param database - */ - public DbSSLSessionCache(DatabaseHelper database) { - this.mDatabaseHelper = database; - } - - public void putSessionData(SSLSession session, byte[] der) { - if (mDatabaseHelper == null) { - return; - } - synchronized (this.getClass()) { - SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); - if (mExternalCache.size() == MAX_CACHE_SIZE) { - // remove oldest. - // TODO: check if the new one is in cached already ( i.e. update ). - Cursor byTime = mDatabaseHelper.getReadableDatabase().query(SSL_CACHE_TABLE, - PROJECTION, null, null, null, null, SSL_CACHE_TIME_SEC); - if (byTime.moveToFirst()) { - // TODO: can I do byTime.deleteRow() ? - String hostPort = byTime.getString(SSL_CACHE_HOSTPORT_COL); - db.delete(SSL_CACHE_TABLE, - SSL_CACHE_HOSTPORT + "= ?" , new String[] { hostPort }); - mExternalCache.remove(hostPort); - } else { - Log.w(TAG, "No rows found"); - // something is wrong, clear it - clear(); - } - } - // Serialize native session to standard DER encoding - long t0 = System.currentTimeMillis(); - - String b64 = new String(Base64.encodeBase64(der)); - String key = session.getPeerHost() + ":" + session.getPeerPort(); - - ContentValues values = new ContentValues(); - values.put(SSL_CACHE_HOSTPORT, key); - values.put(SSL_CACHE_SESSION, b64); - values.put(SSL_CACHE_TIME_SEC, System.currentTimeMillis() / 1000); - - mExternalCache.put(key, der); - - try { - db.insert(SSL_CACHE_TABLE, null /*nullColumnHack */ , values); - } catch(SQLException ex) { - // Ignore - nothing we can do to recover, and caller shouldn't - // be affected. - Log.w(TAG, "Ignoring SQL exception when caching session", ex); - } - if (Log.isLoggable(TAG, Log.DEBUG)) { - long t1 = System.currentTimeMillis(); - Log.d(TAG, "New SSL session " + session.getPeerHost() + - " DER len: " + der.length + " " + (t1 - t0)); - } - } - - } - - public byte[] getSessionData(String host, int port) { - // Current (simple) implementation does a single lookup to DB, then saves - // all entries to the cache. - - // This works for google services - i.e. small number of certs. - // If we extend this to all processes - we should hold a separate cache - // or do lookups to DB each time. - if (mDatabaseHelper == null) { - return null; - } - synchronized(this.getClass()) { - if (mNeedsCacheLoad) { - // Don't try to load again, if something is wrong on the first - // request it'll likely be wrong each time. - mNeedsCacheLoad = false; - long t0 = System.currentTimeMillis(); - - Cursor cur = null; - try { - cur = mDatabaseHelper.getReadableDatabase().query(SSL_CACHE_TABLE, - PROJECTION, null, null, null, null, null); - if (cur.moveToFirst()) { - do { - String hostPort = cur.getString(SSL_CACHE_HOSTPORT_COL); - String value = cur.getString(SSL_CACHE_SESSION_COL); - - if (hostPort == null || value == null) { - continue; - } - // TODO: blob support ? - byte[] der = Base64.decodeBase64(value.getBytes()); - mExternalCache.put(hostPort, der); - } while (cur.moveToNext()); - - } - } catch (SQLException ex) { - Log.d(TAG, "Error loading SSL cached entries ", ex); - } finally { - if (cur != null) { - cur.close(); - } - if (Log.isLoggable(TAG, Log.DEBUG)) { - long t1 = System.currentTimeMillis(); - Log.d(TAG, "LOADED CACHED SSL " + (t1 - t0) + " ms"); - } - } - } - - String key = host + ":" + port; - - return mExternalCache.get(key); - } - } - - /** - * Reset the database and internal state. - * Used for testing or to free space. - */ - public void clear() { - synchronized(this) { - try { - mExternalCache.clear(); - mNeedsCacheLoad = true; - mDatabaseHelper.getWritableDatabase().delete(SSL_CACHE_TABLE, - null, null); - } catch (SQLException ex) { - Log.d(TAG, "Error removing SSL cached entries ", ex); - // ignore - nothing we can do about it - } - } - } - - public byte[] getSessionData(byte[] id) { - // We support client side only - the cache will do nothing for - // server-side sessions. - return null; - } - - /** Visible for testing. - */ - public static class DatabaseHelper extends SQLiteOpenHelper { - - public DatabaseHelper(Context context) { - super(context, DATABASE_NAME, null /* factory */, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + SSL_CACHE_TABLE + " (" + - SSL_CACHE_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - SSL_CACHE_HOSTPORT + " TEXT UNIQUE ON CONFLICT REPLACE," + - SSL_CACHE_SESSION + " TEXT," + - SSL_CACHE_TIME_SEC + " INTEGER" + - ");"); - - // No index - we load on startup, index would slow down inserts. - // If we want to scale this to lots of rows - we could use - // index, but then we'll hit DB a bit too often ( including - // negative hits ) - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - db.execSQL("DROP TABLE IF EXISTS " + SSL_CACHE_TABLE ); - onCreate(db); - } - - } - -} diff --git a/core/java/com/android/internal/net/SSLSessionCache.java b/core/java/com/android/internal/net/SSLSessionCache.java deleted file mode 100644 index ec02fe5..0000000 --- a/core/java/com/android/internal/net/SSLSessionCache.java +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2009 The Android Open Source Project -package com.android.internal.net; - -import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache; -import org.apache.harmony.xnet.provider.jsse.SSLContextImpl; - -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; - -import android.content.ContentResolver; -import android.content.Context; -import android.provider.Settings; -import android.util.Log; - -/** - * Utility class to configure SSL session caching. - * - * - * - * {@hide} - */ -public class SSLSessionCache { - private static final String TAG = "SSLSessionCache"; - - private static final String CACHE_TYPE_DB = "db"; - - private static boolean sInitializationDone = false; - - // One per process - private static DbSSLSessionCache sDbCache; - - /** - * Check settings for ssl session caching. - * - * @return false if disabled. - */ - public static boolean isEnabled(ContentResolver resolver) { - String sslCache = Settings.Gservices.getString(resolver, - Settings.Gservices.SSL_SESSION_CACHE); - - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "enabled " + sslCache); - } - - return CACHE_TYPE_DB.equals(sslCache); - } - - /** - * Return the configured session cache, or null if not enabled. - */ - public static SSLClientSessionCache getSessionCache(Context context) { - if (context == null) { - return null; - } - if (!sInitializationDone) { - if (isEnabled(context.getContentResolver())) { - sDbCache = new DbSSLSessionCache(context); - return sDbCache; - } - // Don't check again. - sInitializationDone = true; - } - return sDbCache; - } - - /** - * Construct the factory, using default constructor if caching is disabled. - * Refactored here to avoid duplication, used in tests. - */ - public static SSLSocketFactory getSocketFactory(Context androidContext, - TrustManager[] trustManager) { - try { - if (androidContext != null) { - SSLClientSessionCache sessionCache = getSessionCache(androidContext); - - if (sessionCache != null) { - SSLContextImpl sslContext = new SSLContextImpl(); - sslContext.engineInit(null /* kms */, - trustManager, new java.security.SecureRandom(), - sessionCache, null /* serverCache */); - return sslContext.engineGetSocketFactory(); - } - } - // default - SSLContext context = SSLContext.getInstance("TLS"); - context.init(null, trustManager, new java.security.SecureRandom()); - return context.getSocketFactory(); - - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (KeyManagementException e) { - throw new AssertionError(e); - } - } - - -} diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 7eea8b7..558a122 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -23,7 +23,6 @@ import android.os.ParcelFormatException; import android.os.Parcelable; import android.os.SystemClock; import android.util.Log; -import android.util.Printer; import android.util.SparseArray; import java.io.File; @@ -40,14 +39,14 @@ import java.util.Map; * otherwise. */ public final class BatteryStatsImpl extends BatteryStats { + private static final String TAG = "BatteryStatsImpl"; - private static final boolean DEBUG = false; // In-memory Parcel magic number, used to detect attempts to unmarshall bad data private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 25; + private static final int VERSION = 23; private final File mFile; private final File mBackupFile; @@ -87,11 +86,9 @@ public final class BatteryStatsImpl extends BatteryStats { long mLastRealtime; boolean mScreenOn; - Timer mScreenOnTimer; - - boolean mPhoneOn; - Timer mPhoneOnTimer; - + long mLastScreenOnTimeMillis; + long mBatteryScreenOnTimeMillis; + long mPluggedScreenOnTimeMillis; /** * These provide time bases that discount the time the device is plugged * in to power. @@ -135,45 +132,16 @@ public final class BatteryStatsImpl extends BatteryStats { // Times are in microseconds for better accuracy when dividing by the // lock count, and are in "battery realtime" units. - /** - * The total time we have accumulated since the start of the original - * boot, to the last time something interesting happened in the - * current run. - */ long mTotalTime; - - /** - * The total time we loaded for the previous runs. Subtract this from - * mTotalTime to find the time for the current run of the system. - */ - long mLoadedTime; - - /** - * The run time of the last run of the system, as loaded from the - * saved data. - */ - long mLastTime; - - /** - * The value of mTotalTime when unplug() was last called. Subtract - * this from mTotalTime to find the time since the last unplug from - * power. - */ - long mUnpluggedTime; - - /** - * The last time at which we updated the timer. If mNesting is > 0, - * subtract this from the current battery time to find the amount of - * time we have been running since we last computed an update. - */ + long mLoadedTotalTime; + long mLastTotalTime; long mUpdateTime; /** - * The total time at which the timer was acquired, to determine if - * was actually held for an interesting duration. + * The value of mTotalTime when unplug() was last called, initially 0. */ - long mAcquireTime; - + long mUnpluggedTotalTime; + Timer(int type, ArrayList<Timer> timerPool, ArrayList<Unpluggable> unpluggables, Parcel in) { mType = type; @@ -183,10 +151,10 @@ public final class BatteryStatsImpl extends BatteryStats { mLastCount = in.readInt(); mUnpluggedCount = in.readInt(); mTotalTime = in.readLong(); - mLoadedTime = in.readLong(); - mLastTime = in.readLong(); + mLoadedTotalTime = in.readLong(); + mLastTotalTime = in.readLong(); mUpdateTime = in.readLong(); - mUnpluggedTime = in.readLong(); + mUnpluggedTotalTime = in.readLong(); unpluggables.add(this); } @@ -203,41 +171,21 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(mLastCount); out.writeInt(mUnpluggedCount); out.writeLong(computeRunTimeLocked(batteryRealtime)); - out.writeLong(mLoadedTime); - out.writeLong(mLastTime); + out.writeLong(mLoadedTotalTime); + out.writeLong(mLastTotalTime); out.writeLong(mUpdateTime); - out.writeLong(mUnpluggedTime); + out.writeLong(mUnpluggedTotalTime); } public void unplug(long batteryUptime, long batteryRealtime) { - if (DEBUG && mType < 0) { - Log.v(TAG, "unplug #" + mType + ": realtime=" + batteryRealtime - + " old mUnpluggedTime=" + mUnpluggedTime - + " old mUnpluggedCount=" + mUnpluggedCount); - } - mUnpluggedTime = computeRunTimeLocked(batteryRealtime); + mUnpluggedTotalTime = computeRunTimeLocked(batteryRealtime); mUnpluggedCount = mCount; - if (DEBUG && mType < 0) { - Log.v(TAG, "unplug #" + mType - + ": new mUnpluggedTime=" + mUnpluggedTime - + " new mUnpluggedCount=" + mUnpluggedCount); - } } public void plug(long batteryUptime, long batteryRealtime) { if (mNesting > 0) { - if (DEBUG && mType < 0) { - Log.v(TAG, "plug #" + mType + ": realtime=" + batteryRealtime - + " old mTotalTime=" + mTotalTime - + " old mUpdateTime=" + mUpdateTime); - } mTotalTime = computeRunTimeLocked(batteryRealtime); mUpdateTime = batteryRealtime; - if (DEBUG && mType < 0) { - Log.v(TAG, "plug #" + mType - + ": new mTotalTime=" + mTotalTime - + " old mUpdateTime=" + mUpdateTime); - } } } @@ -259,16 +207,16 @@ public final class BatteryStatsImpl extends BatteryStats { } @Override - public long getTotalTime(long batteryRealtime, int which) { + public long getTotalTime(long now, int which) { long val; if (which == STATS_LAST) { - val = mLastTime; + val = mLastTotalTime; } else { - val = computeRunTimeLocked(batteryRealtime); + val = computeRunTimeLocked(now); if (which == STATS_UNPLUGGED) { - val -= mUnpluggedTime; + val -= mUnpluggedTotalTime; } else if (which != STATS_TOTAL) { - val -= mLoadedTime; + val -= mLoadedTotalTime; } } @@ -297,32 +245,22 @@ public final class BatteryStatsImpl extends BatteryStats { + " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount + " mUnpluggedCount=" + mUnpluggedCount); Log.i("foo", "mTotalTime=" + mTotalTime - + " mLoadedTime=" + mLoadedTime); - Log.i("foo", "mLastTime=" + mLastTime - + " mUnpluggedTime=" + mUnpluggedTime); - Log.i("foo", "mUpdateTime=" + mUpdateTime - + " mAcquireTime=" + mAcquireTime); + + " mLoadedTotalTime=" + mLoadedTotalTime); + Log.i("foo", "mLastTotalTime=" + mLastTotalTime + + " mUpdateTime=" + mUpdateTime); } void startRunningLocked(BatteryStatsImpl stats) { if (mNesting++ == 0) { mUpdateTime = stats.getBatteryRealtimeLocked( SystemClock.elapsedRealtime() * 1000); - if (mTimerPool != null) { - // Accumulate time to all currently active timers before adding - // this new one to the pool. - refreshTimersLocked(stats, mTimerPool); - // Add this timer to the active pool - mTimerPool.add(this); - } + // Accumulate time to all currently active timers before adding + // this new one to the pool. + refreshTimersLocked(stats, mTimerPool); + // Add this timer to the active pool + mTimerPool.add(this); // Increment the count mCount++; - mAcquireTime = mTotalTime; - if (DEBUG && mType < 0) { - Log.v(TAG, "start #" + mType + ": mUpdateTime=" + mUpdateTime - + " mTotalTime=" + mTotalTime + " mCount=" + mCount - + " mAcquireTime=" + mAcquireTime); - } } } @@ -332,31 +270,11 @@ public final class BatteryStatsImpl extends BatteryStats { return; } if (--mNesting == 0) { - if (mTimerPool != null) { - // Accumulate time to all active counters, scaled by the total - // active in the pool, before taking this one out of the pool. - refreshTimersLocked(stats, mTimerPool); - // Remove this timer from the active pool - mTimerPool.remove(this); - } else { - final long realtime = SystemClock.elapsedRealtime() * 1000; - final long batteryRealtime = stats.getBatteryRealtimeLocked(realtime); - mNesting = 1; - mTotalTime = computeRunTimeLocked(batteryRealtime); - mNesting = 0; - } - - if (DEBUG && mType < 0) { - Log.v(TAG, "stop #" + mType + ": mUpdateTime=" + mUpdateTime - + " mTotalTime=" + mTotalTime + " mCount=" + mCount - + " mAcquireTime=" + mAcquireTime); - } - - if (mTotalTime == mAcquireTime) { - // If there was no change in the time, then discard this - // count. A somewhat cheezy strategy, but hey. - mCount--; - } + // Accumulate time to all active counters, scaled by the total + // active in the pool, before taking this one out of the pool. + refreshTimersLocked(stats, mTimerPool); + // Remove this timer from the active pool + mTimerPool.remove(this); } } @@ -379,28 +297,24 @@ public final class BatteryStatsImpl extends BatteryStats { private long computeRunTimeLocked(long curBatteryRealtime) { return mTotalTime + (mNesting > 0 - ? (curBatteryRealtime - mUpdateTime) - / (mTimerPool != null ? mTimerPool.size() : 1) - : 0); + ? (curBatteryRealtime - mUpdateTime) / mTimerPool.size() : 0); } - void writeSummaryFromParcelLocked(Parcel out, long batteryRealtime) { - long runTime = computeRunTimeLocked(batteryRealtime); + void writeSummaryFromParcelLocked(Parcel out, long curBatteryUptime) { + long runTime = computeRunTimeLocked(curBatteryUptime); // Divide by 1000 for backwards compatibility out.writeLong((runTime + 500) / 1000); - out.writeLong(((runTime - mLoadedTime) + 500) / 1000); + out.writeLong(((runTime - mLoadedTotalTime) + 500) / 1000); out.writeInt(mCount); out.writeInt(mCount - mLoadedCount); } void readSummaryFromParcelLocked(Parcel in) { // Multiply by 1000 for backwards compatibility - mTotalTime = mLoadedTime = in.readLong() * 1000; - mLastTime = in.readLong() * 1000; - mUnpluggedTime = mTotalTime; + mTotalTime = mLoadedTotalTime = in.readLong() * 1000; + mLastTotalTime = in.readLong(); mCount = mLoadedCount = in.readInt(); mLastCount = in.readInt(); - mUnpluggedCount = mCount; mNesting = 0; } } @@ -443,40 +357,47 @@ public final class BatteryStatsImpl extends BatteryStats { mUidStats.get(uid).noteStopGps(); } - public void noteScreenOnLocked() { + /** + * When the device screen or battery state changes, update the appropriate "screen on time" + * counter. + */ + private void updateScreenOnTimeLocked(boolean screenOn) { if (!mScreenOn) { - mScreenOn = true; - mScreenOnTimer.startRunningLocked(this); + Log.w(TAG, "updateScreenOnTime without mScreenOn, ignored"); + return; } - } - - public void noteScreenOffLocked() { - if (mScreenOn) { - mScreenOn = false; - mScreenOnTimer.stopRunningLocked(this); + long now = SystemClock.elapsedRealtime(); + long elapsed = now - mLastScreenOnTimeMillis; + if (mOnBattery) { + mBatteryScreenOnTimeMillis += elapsed; + } else { + mPluggedScreenOnTimeMillis += elapsed; + } + if (screenOn) { + mLastScreenOnTimeMillis = now; } } - public void notePhoneOnLocked() { - if (!mPhoneOn) { - mPhoneOn = true; - mPhoneOnTimer.startRunningLocked(this); - } + public void noteScreenOnLocked() { + mScreenOn = true; + mLastScreenOnTimeMillis = SystemClock.elapsedRealtime(); } - public void notePhoneOffLocked() { - if (mPhoneOn) { - mPhoneOn = false; - mPhoneOnTimer.stopRunningLocked(this); + public void noteScreenOffLocked() { + if (!mScreenOn) { + Log.w(TAG, "noteScreenOff without mScreenOn, ignored"); + return; } + updateScreenOnTimeLocked(false); + mScreenOn = false; } - @Override public long getScreenOnTime(long batteryRealtime, int which) { - return mScreenOnTimer.getTotalTime(batteryRealtime, which); + @Override public long getBatteryScreenOnTime() { + return mBatteryScreenOnTimeMillis; } - @Override public long getPhoneOnTime(long batteryRealtime, int which) { - return mPhoneOnTimer.getTotalTime(batteryRealtime, which); + @Override public long getPluggedScreenOnTime() { + return mPluggedScreenOnTimeMillis; } @Override public boolean getIsOnBattery() { @@ -1460,8 +1381,6 @@ public final class BatteryStatsImpl extends BatteryStats { mFile = new File(filename); mBackupFile = new File(filename + ".bak"); mStartCount++; - mScreenOnTimer = new Timer(-1, null, mUnpluggables); - mPhoneOnTimer = new Timer(-2, null, mUnpluggables); mOnBattery = mOnBatteryInternal = false; mTrackBatteryPastUptime = 0; mTrackBatteryPastRealtime = 0; @@ -1488,6 +1407,10 @@ public final class BatteryStatsImpl extends BatteryStats { public void setOnBattery(boolean onBattery) { synchronized(this) { if (mOnBattery != onBattery) { + if (mScreenOn) { + updateScreenOnTimeLocked(true); + } + mOnBattery = mOnBatteryInternal = onBattery; long uptime = SystemClock.uptimeMillis() * 1000; @@ -1502,7 +1425,7 @@ public final class BatteryStatsImpl extends BatteryStats { } else { mTrackBatteryPastUptime += uptime - mTrackBatteryUptimeStart; mTrackBatteryPastRealtime += realtime - mTrackBatteryRealtimeStart; - doPlug(getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime)); + doPlug(mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime); } if ((mLastWriteTime + (60 * 1000)) < mSecRealtime) { if (mFile != null) { @@ -1524,10 +1447,10 @@ public final class BatteryStatsImpl extends BatteryStats { @Override public long computeUptime(long curTime, int which) { switch (which) { - case STATS_TOTAL: return mUptime + (curTime-mUptimeStart); - case STATS_LAST: return mLastUptime; - case STATS_CURRENT: return (curTime-mUptimeStart); - case STATS_UNPLUGGED: return (curTime-mTrackBatteryUptimeStart); + case STATS_TOTAL: return mUptime + (curTime-mUptimeStart); + case STATS_LAST: return mLastUptime; + case STATS_CURRENT: return (curTime-mUptimeStart); + case STATS_UNPLUGGED: return (curTime-mTrackBatteryUptimeStart); } return 0; } @@ -1535,25 +1458,26 @@ public final class BatteryStatsImpl extends BatteryStats { @Override public long computeRealtime(long curTime, int which) { switch (which) { - case STATS_TOTAL: return mRealtime + (curTime-mRealtimeStart); - case STATS_LAST: return mLastRealtime; - case STATS_CURRENT: return (curTime-mRealtimeStart); - case STATS_UNPLUGGED: return (curTime-mTrackBatteryRealtimeStart); + case STATS_TOTAL: return mRealtime + (curTime-mRealtimeStart); + case STATS_LAST: return mLastRealtime; + case STATS_CURRENT: return (curTime-mRealtimeStart); + case STATS_UNPLUGGED: return (curTime-mTrackBatteryRealtimeStart); } return 0; } @Override public long computeBatteryUptime(long curTime, int which) { + long uptime = getBatteryUptime(curTime); switch (which) { - case STATS_TOTAL: - return mBatteryUptime + getBatteryUptime(curTime); - case STATS_LAST: - return mBatteryLastUptime; - case STATS_CURRENT: - return getBatteryUptime(curTime); - case STATS_UNPLUGGED: - return getBatteryUptimeLocked(curTime) - mUnpluggedBatteryUptime; + case STATS_TOTAL: + return mBatteryUptime + uptime; + case STATS_LAST: + return mBatteryLastUptime; + case STATS_CURRENT: + return uptime; + case STATS_UNPLUGGED: + return getBatteryUptimeLocked(curTime) - mUnpluggedBatteryUptime; } return 0; } @@ -1561,14 +1485,14 @@ public final class BatteryStatsImpl extends BatteryStats { @Override public long computeBatteryRealtime(long curTime, int which) { switch (which) { - case STATS_TOTAL: - return mBatteryRealtime + getBatteryRealtimeLocked(curTime); - case STATS_LAST: - return mBatteryLastRealtime; - case STATS_CURRENT: - return getBatteryRealtimeLocked(curTime); - case STATS_UNPLUGGED: - return getBatteryRealtimeLocked(curTime) - mUnpluggedBatteryRealtime; + case STATS_TOTAL: + return mBatteryRealtime + getBatteryRealtimeLocked(curTime); + case STATS_LAST: + return mBatteryLastRealtime; + case STATS_CURRENT: + return getBatteryRealtimeLocked(curTime); + case STATS_UNPLUGGED: + return getBatteryRealtimeLocked(curTime) - mUnpluggedBatteryRealtime; } return 0; } @@ -1764,10 +1688,9 @@ public final class BatteryStatsImpl extends BatteryStats { mLastRealtime = in.readLong(); mStartCount++; + mBatteryScreenOnTimeMillis = in.readLong(); + mPluggedScreenOnTimeMillis = in.readLong(); mScreenOn = false; - mScreenOnTimer.readSummaryFromParcelLocked(in); - mPhoneOn = false; - mPhoneOnTimer.readSummaryFromParcelLocked(in); final int NU = in.readInt(); for (int iu = 0; iu < NU; iu++) { @@ -1841,10 +1764,9 @@ public final class BatteryStatsImpl extends BatteryStats { * @param out the Parcel to be written to. */ public void writeSummaryToParcel(Parcel out) { + final long NOW = getBatteryUptimeLocked(); final long NOW_SYS = SystemClock.uptimeMillis() * 1000; final long NOWREAL_SYS = SystemClock.elapsedRealtime() * 1000; - final long NOW = getBatteryUptimeLocked(NOW_SYS); - final long NOWREAL = getBatteryRealtimeLocked(NOWREAL_SYS); out.writeInt(VERSION); @@ -1858,8 +1780,8 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeLong(computeRealtime(NOWREAL_SYS, STATS_TOTAL)); out.writeLong(computeRealtime(NOWREAL_SYS, STATS_CURRENT)); - mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL); - mPhoneOnTimer.writeSummaryFromParcelLocked(out, NOWREAL); + out.writeLong(mBatteryScreenOnTimeMillis); + out.writeLong(mPluggedScreenOnTimeMillis); final int NU = mUidStats.size(); out.writeInt(NU); @@ -1876,19 +1798,19 @@ public final class BatteryStatsImpl extends BatteryStats { Uid.Wakelock wl = ent.getValue(); if (wl.mTimerFull != null) { out.writeInt(1); - wl.mTimerFull.writeSummaryFromParcelLocked(out, NOWREAL); + wl.mTimerFull.writeSummaryFromParcelLocked(out, NOW); } else { out.writeInt(0); } if (wl.mTimerPartial != null) { out.writeInt(1); - wl.mTimerPartial.writeSummaryFromParcelLocked(out, NOWREAL); + wl.mTimerPartial.writeSummaryFromParcelLocked(out, NOW); } else { out.writeInt(0); } if (wl.mTimerWindow != null) { out.writeInt(1); - wl.mTimerWindow.writeSummaryFromParcelLocked(out, NOWREAL); + wl.mTimerWindow.writeSummaryFromParcelLocked(out, NOW); } else { out.writeInt(0); } @@ -1904,7 +1826,7 @@ public final class BatteryStatsImpl extends BatteryStats { Uid.Sensor se = ent.getValue(); if (se.mTimer != null) { out.writeInt(1); - se.mTimer.writeSummaryFromParcelLocked(out, NOWREAL); + se.mTimer.writeSummaryFromParcelLocked(out, NOW); } else { out.writeInt(0); } @@ -1975,10 +1897,9 @@ public final class BatteryStatsImpl extends BatteryStats { mBatteryLastUptime = in.readLong(); mBatteryRealtime = in.readLong(); mBatteryLastRealtime = in.readLong(); + mBatteryScreenOnTimeMillis = in.readLong(); + mPluggedScreenOnTimeMillis = in.readLong(); mScreenOn = false; - mScreenOnTimer = new Timer(-1, null, mUnpluggables, in); - mPhoneOn = false; - mPhoneOnTimer = new Timer(-2, null, mUnpluggables, in); mUptime = in.readLong(); mUptimeStart = in.readLong(); mLastUptime = in.readLong(); @@ -1991,8 +1912,6 @@ public final class BatteryStatsImpl extends BatteryStats { mTrackBatteryUptimeStart = in.readLong(); mTrackBatteryPastRealtime = in.readLong(); mTrackBatteryRealtimeStart = in.readLong(); - mUnpluggedBatteryUptime = in.readLong(); - mUnpluggedBatteryRealtime = in.readLong(); mLastWriteTime = in.readLong(); mPartialTimers.clear(); @@ -2026,8 +1945,8 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeLong(mBatteryLastUptime); out.writeLong(mBatteryRealtime); out.writeLong(mBatteryLastRealtime); - mScreenOnTimer.writeToParcel(out, batteryRealtime); - mPhoneOnTimer.writeToParcel(out, batteryRealtime); + out.writeLong(mBatteryScreenOnTimeMillis); + out.writeLong(mPluggedScreenOnTimeMillis); out.writeLong(mUptime); out.writeLong(mUptimeStart); out.writeLong(mLastUptime); @@ -2035,12 +1954,10 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeLong(mRealtimeStart); out.writeLong(mLastRealtime); out.writeInt(mOnBattery ? 1 : 0); - out.writeLong(batteryUptime); + out.writeLong(mTrackBatteryPastUptime); out.writeLong(mTrackBatteryUptimeStart); - out.writeLong(batteryRealtime); + out.writeLong(mTrackBatteryPastRealtime); out.writeLong(mTrackBatteryRealtimeStart); - out.writeLong(mUnpluggedBatteryUptime); - out.writeLong(mUnpluggedBatteryRealtime); out.writeLong(mLastWriteTime); int size = mUidStats.size(); @@ -2063,14 +1980,4 @@ public final class BatteryStatsImpl extends BatteryStats { return new BatteryStatsImpl[size]; } }; - - public void dumpLocked(Printer pw) { - if (DEBUG) { - Log.i(TAG, "*** Screen timer:"); - mScreenOnTimer.logState(); - Log.i(TAG, "*** Phone timer:"); - mPhoneOnTimer.logState(); - } - super.dumpLocked(pw); - } } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index ac8b589..f21b62f 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -19,7 +19,6 @@ package com.android.internal.os; import android.content.pm.ActivityInfo; import android.content.res.Resources; import android.content.res.TypedArray; -import android.content.res.ColorStateList; import android.graphics.drawable.Drawable; import android.net.LocalServerSocket; import android.os.Debug; @@ -336,18 +335,32 @@ public class ZygoteInit { mResources.startPreloading(); if (PRELOAD_RESOURCES) { Log.i(TAG, "Preloading resources..."); - long startTime = SystemClock.uptimeMillis(); TypedArray ar = mResources.obtainTypedArray( com.android.internal.R.array.preloaded_drawables); - int N = preloadDrawables(runtime, ar); - Log.i(TAG, "...preloaded " + N + " resources in " - + (SystemClock.uptimeMillis()-startTime) + "ms."); - - startTime = SystemClock.uptimeMillis(); - ar = mResources.obtainTypedArray( - com.android.internal.R.array.preloaded_color_state_lists); - N = preloadColorStateLists(runtime, ar); + int N = ar.length(); + for (int i=0; i<N; i++) { + if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) { + if (Config.LOGV) { + Log.v(TAG, " GC at " + Debug.getGlobalAllocSize()); + } + runtime.gcSoftReferences(); + runtime.runFinalizationSync(); + Debug.resetGlobalAllocSize(); + } + int id = ar.getResourceId(i, 0); + if (Config.LOGV) { + Log.v(TAG, "Preloading resource #" + Integer.toHexString(id)); + } + if (id != 0) { + Drawable dr = mResources.getDrawable(id); + if ((dr.getChangingConfigurations()&~ActivityInfo.CONFIG_FONT_SCALE) != 0) { + Log.w(TAG, "Preloaded drawable resource #0x" + + Integer.toHexString(id) + + " that varies with configuration!!"); + } + } + } Log.i(TAG, "...preloaded " + N + " resources in " + (SystemClock.uptimeMillis()-startTime) + "ms."); } @@ -359,56 +372,6 @@ public class ZygoteInit { } } - private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) { - int N = ar.length(); - for (int i=0; i<N; i++) { - if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) { - if (Config.LOGV) { - Log.v(TAG, " GC at " + Debug.getGlobalAllocSize()); - } - runtime.gcSoftReferences(); - runtime.runFinalizationSync(); - Debug.resetGlobalAllocSize(); - } - int id = ar.getResourceId(i, 0); - if (Config.LOGV) { - Log.v(TAG, "Preloading resource #" + Integer.toHexString(id)); - } - if (id != 0) { - mResources.getColorStateList(id); - } - } - return N; - } - - - private static int preloadDrawables(VMRuntime runtime, TypedArray ar) { - int N = ar.length(); - for (int i=0; i<N; i++) { - if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) { - if (Config.LOGV) { - Log.v(TAG, " GC at " + Debug.getGlobalAllocSize()); - } - runtime.gcSoftReferences(); - runtime.runFinalizationSync(); - Debug.resetGlobalAllocSize(); - } - int id = ar.getResourceId(i, 0); - if (Config.LOGV) { - Log.v(TAG, "Preloading resource #" + Integer.toHexString(id)); - } - if (id != 0) { - Drawable dr = mResources.getDrawable(id); - if ((dr.getChangingConfigurations()&~ActivityInfo.CONFIG_FONT_SCALE) != 0) { - Log.w(TAG, "Preloaded drawable resource #0x" - + Integer.toHexString(id) - + " (" + ar.getString(i) + ") that varies with configuration!!"); - } - } - } - return N; - } - /** * Runs several special GCs to try to clean up a few generations of * softly- and final-reachable objects, along with any other garbage. diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 8e65177..4f98cee 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -23,8 +23,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_COMMIT_TEXT = 50; private static final int DO_COMMIT_COMPLETION = 55; private static final int DO_SET_SELECTION = 57; - private static final int DO_PERFORM_EDITOR_ACTION = 58; - private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59; + private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 58; private static final int DO_SET_COMPOSING_TEXT = 60; private static final int DO_FINISH_COMPOSING_TEXT = 65; private static final int DO_SEND_KEY_EVENT = 70; @@ -98,10 +97,6 @@ public class IInputConnectionWrapper extends IInputContext.Stub { dispatchMessage(obtainMessageII(DO_SET_SELECTION, start, end)); } - public void performEditorAction(int id) { - dispatchMessage(obtainMessageII(DO_PERFORM_EDITOR_ACTION, id, 0)); - } - public void performContextMenuAction(int id) { dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0)); } @@ -240,15 +235,6 @@ public class IInputConnectionWrapper extends IInputContext.Stub { ic.setSelection(msg.arg1, msg.arg2); return; } - case DO_PERFORM_EDITOR_ACTION: { - InputConnection ic = mInputConnection.get(); - if (ic == null || !isActive()) { - Log.w(TAG, "performEditorAction on inactive InputConnection"); - return; - } - ic.performEditorAction(msg.arg1); - return; - } case DO_PERFORM_CONTEXT_MENU_ACTION: { InputConnection ic = mInputConnection.get(); if (ic == null || !isActive()) { diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl index 02cb9e4..02b6044 100644 --- a/core/java/com/android/internal/view/IInputContext.aidl +++ b/core/java/com/android/internal/view/IInputContext.aidl @@ -50,8 +50,6 @@ import com.android.internal.view.IInputContextCallback; void setSelection(int start, int end); - void performEditorAction(int actionCode); - void performContextMenuAction(int id); void beginBatchEdit(); diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index b92cb45..32d9f3d 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -250,15 +250,6 @@ public class InputConnectionWrapper implements InputConnection { } } - public boolean performEditorAction(int actionCode) { - try { - mIInputContext.performEditorAction(actionCode); - return true; - } catch (RemoteException e) { - return false; - } - } - public boolean performContextMenuAction(int id) { try { mIInputContext.performContextMenuAction(id); diff --git a/core/java/com/android/internal/view/menu/ExpandedMenuView.java b/core/java/com/android/internal/view/menu/ExpandedMenuView.java index 9e4b4ce..c16c165 100644 --- a/core/java/com/android/internal/view/menu/ExpandedMenuView.java +++ b/core/java/com/android/internal/view/menu/ExpandedMenuView.java @@ -80,11 +80,6 @@ public final class ExpandedMenuView extends ListView implements ItemInvoker, Men setChildrenDrawingCacheEnabled(false); } - @Override - protected boolean recycleOnMeasure() { - return false; - } - public boolean invokeItem(MenuItemImpl item) { return mMenu.performItemAction(item, 0); } diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index f2ec064..44cf0ed 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -59,14 +59,7 @@ public class EditableInputConnection extends BaseInputConnection { final Editable content = getEditable(); if (content == null) return false; KeyListener kl = mTextView.getKeyListener(); - if (kl != null) { - try { - kl.clearMetaKeyState(mTextView, content, states); - } catch (AbstractMethodError e) { - // This is an old listener that doesn't implement the - // new method. - } - } + if (kl != null) kl.clearMetaKeyState(mTextView, content, states); return true; } @@ -78,12 +71,6 @@ public class EditableInputConnection extends BaseInputConnection { return true; } - public boolean performEditorAction(int actionCode) { - if (DEBUG) Log.v(TAG, "performEditorAction " + actionCode); - mTextView.onEditorAction(actionCode); - return true; - } - public boolean performContextMenuAction(int id) { if (DEBUG) Log.v(TAG, "performContextMenuAction " + id); mTextView.beginBatchEdit(); diff --git a/core/java/com/android/internal/widget/TextProgressBar.java b/core/java/com/android/internal/widget/TextProgressBar.java index aee7b76..5bf4601 100644 --- a/core/java/com/android/internal/widget/TextProgressBar.java +++ b/core/java/com/android/internal/widget/TextProgressBar.java @@ -115,10 +115,8 @@ public class TextProgressBar extends RelativeLayout implements OnChronometerTick // Update the ProgressBar maximum relative to Chronometer base mDuration = (int) (durationBase - mChronometer.getBase()); - if (mDuration <= 0) { - mDuration = 1; - } mProgressBar.setMax(mDuration); + } /** diff --git a/core/java/com/google/android/gdata/client/AndroidGDataClient.java b/core/java/com/google/android/gdata/client/AndroidGDataClient.java index fe7d860..1d8e9c5 100644 --- a/core/java/com/google/android/gdata/client/AndroidGDataClient.java +++ b/core/java/com/google/android/gdata/client/AndroidGDataClient.java @@ -21,7 +21,6 @@ import org.apache.http.entity.InputStreamEntity; import org.apache.http.entity.AbstractHttpEntity; import android.content.ContentResolver; -import android.content.Context; import android.net.http.AndroidHttpClient; import android.text.TextUtils; import android.util.Config; @@ -118,28 +117,16 @@ public class AndroidGDataClient implements GDataClient { } /** - * @deprecated Use AndroidGDAtaClient(Context) instead. - */ - public AndroidGDataClient(ContentResolver resolver) { - mHttpClient = new GoogleHttpClient(resolver, USER_AGENT_APP_VERSION, - true /* gzip capable */); - mHttpClient.enableCurlLogging(TAG, Log.VERBOSE); - mResolver = resolver; - } - - /** * Creates a new AndroidGDataClient. * - * @param context The ContentResolver to get URL rewriting rules from + * @param resolver The ContentResolver to get URL rewriting rules from * through the Android proxy server, using null to indicate not using proxy. - * The context will also be used by GoogleHttpClient for configuration of - * SSL session persistence. */ - public AndroidGDataClient(Context context) { - mHttpClient = new GoogleHttpClient(context, USER_AGENT_APP_VERSION, + public AndroidGDataClient(ContentResolver resolver) { + mHttpClient = new GoogleHttpClient(resolver, USER_AGENT_APP_VERSION, true /* gzip capable */); mHttpClient.enableCurlLogging(TAG, Log.VERBOSE); - mResolver = context.getContentResolver(); + mResolver = resolver; } public void close() { diff --git a/core/java/com/google/android/net/GoogleHttpClient.java b/core/java/com/google/android/net/GoogleHttpClient.java index ae78ba8..2fcb0c3 100644 --- a/core/java/com/google/android/net/GoogleHttpClient.java +++ b/core/java/com/google/android/net/GoogleHttpClient.java @@ -16,12 +16,8 @@ package com.google.android.net; -import com.android.internal.net.DbSSLSessionCache; -import com.android.internal.net.SSLSessionCache; - import android.content.ContentResolver; import android.content.ContentValues; -import android.content.Context; import android.net.http.AndroidHttpClient; import android.os.Build; import android.os.NetStat; @@ -83,24 +79,6 @@ public class GoogleHttpClient implements HttpClient { } /** - * GoogleHttpClient(Context, String, boolean) - without SSL session - * persistence. - * - * @deprecated use Context instead of ContentResolver. - */ - public GoogleHttpClient(ContentResolver resolver, String appAndVersion, - boolean gzipCapable) { - String userAgent = appAndVersion - + " (" + Build.DEVICE + " " + Build.ID + ")"; - if (gzipCapable) { - userAgent = userAgent + "; gzip"; - } - mClient = AndroidHttpClient.newInstance(userAgent); - mResolver = resolver; - mUserAgent = userAgent; - } - - /** * Create an HTTP client. Normaly this client is shared throughout an app. * The HTTP client will construct its User-Agent as follows: * @@ -109,10 +87,7 @@ public class GoogleHttpClient implements HttpClient { * <appAndVersion> (<build device> <build id>); gzip * (if gzip capable) * - * The context has settings for URL rewriting rules and is used to enable - * SSL session persistence. - * - * @param context application context. + * @param resolver to use for acccessing URL rewriting rules. * @param appAndVersion Base app and version to use in the User-Agent. * e.g., "MyApp/1.0" * @param gzipCapable Whether or not this client is able to consume gzip'd @@ -120,18 +95,15 @@ public class GoogleHttpClient implements HttpClient { * headers. Needed because Google servers require gzip in the User-Agent * in order to return gzip'd content. */ - public GoogleHttpClient(Context context, String appAndVersion, - boolean gzipCapable) { - + public GoogleHttpClient(ContentResolver resolver, String appAndVersion, + boolean gzipCapable) { String userAgent = appAndVersion + " (" + Build.DEVICE + " " + Build.ID + ")"; if (gzipCapable) { userAgent = userAgent + "; gzip"; } - mClient = AndroidHttpClient.newInstance(userAgent, - SSLSessionCache.getSessionCache(context)); - - mResolver = context.getContentResolver(); + mClient = AndroidHttpClient.newInstance(userAgent); + mResolver = resolver; mUserAgent = userAgent; } |