diff options
Diffstat (limited to 'core/java/android')
21 files changed, 533 insertions, 160 deletions
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 3d0b7d8..523a78d 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -98,6 +98,8 @@ public class ActivityOptions { public static final int ANIM_SCALE_UP = 2; /** @hide */ public static final int ANIM_THUMBNAIL = 3; + /** @hide */ + public static final int ANIM_THUMBNAIL_DELAYED = 4; private String mPackageName; private int mAnimationType = ANIM_NONE; @@ -219,9 +221,38 @@ public class ActivityOptions { */ public static ActivityOptions makeThumbnailScaleUpAnimation(View source, Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) { + return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, listener, false); + } + + /** + * Create an ActivityOptions specifying an animation where a thumbnail + * is scaled from a given position to the new activity window that is + * being started. Before the animation, there is a short delay. + * + * @param source The View that this thumbnail is animating from. This + * defines the coordinate space for <var>startX</var> and <var>startY</var>. + * @param thumbnail The bitmap that will be shown as the initial thumbnail + * of the animation. + * @param startX The x starting location of the bitmap, relative to <var>source</var>. + * @param startY The y starting location of the bitmap, relative to <var>source</var>. + * @param listener Optional OnAnimationStartedListener to find out when the + * requested animation has started running. If for some reason the animation + * is not executed, the callback will happen immediately. + * @return Returns a new ActivityOptions object that you can use to + * supply these options as the options Bundle when starting an activity. + * @hide + */ + public static ActivityOptions makeDelayedThumbnailScaleUpAnimation(View source, + Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) { + return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, listener, true); + } + + private static ActivityOptions makeThumbnailScaleUpAnimation(View source, + Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener, + boolean delayed) { ActivityOptions opts = new ActivityOptions(); opts.mPackageName = source.getContext().getPackageName(); - opts.mAnimationType = ANIM_THUMBNAIL; + opts.mAnimationType = delayed ? ANIM_THUMBNAIL_DELAYED : ANIM_THUMBNAIL; opts.mThumbnail = thumbnail; int[] pts = new int[2]; source.getLocationOnScreen(pts); @@ -258,7 +289,8 @@ public class ActivityOptions { mStartY = opts.getInt(KEY_ANIM_START_Y, 0); mStartWidth = opts.getInt(KEY_ANIM_START_WIDTH, 0); mStartHeight = opts.getInt(KEY_ANIM_START_HEIGHT, 0); - } else if (mAnimationType == ANIM_THUMBNAIL) { + } else if (mAnimationType == ANIM_THUMBNAIL || + mAnimationType == ANIM_THUMBNAIL_DELAYED) { mThumbnail = (Bitmap)opts.getParcelable(KEY_ANIM_THUMBNAIL); mStartX = opts.getInt(KEY_ANIM_START_X, 0); mStartY = opts.getInt(KEY_ANIM_START_Y, 0); @@ -359,6 +391,7 @@ public class ActivityOptions { mStartHeight = otherOptions.mStartHeight; break; case ANIM_THUMBNAIL: + case ANIM_THUMBNAIL_DELAYED: mAnimationType = otherOptions.mAnimationType; mThumbnail = otherOptions.mThumbnail; mStartX = otherOptions.mStartX; @@ -401,6 +434,7 @@ public class ActivityOptions { b.putInt(KEY_ANIM_START_HEIGHT, mStartHeight); break; case ANIM_THUMBNAIL: + case ANIM_THUMBNAIL_DELAYED: b.putInt(KEY_ANIM_TYPE, mAnimationType); b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail); b.putInt(KEY_ANIM_START_X, mStartX); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index f962259..0c47069 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -390,21 +390,25 @@ public class Notification implements Parcelable public int priority; /** + * @hide * Notification type: incoming call (voice or video) or similar synchronous communication request. */ public static final String KIND_CALL = "android.call"; /** + * @hide * Notification type: incoming direct message (SMS, instant message, etc.). */ public static final String KIND_MESSAGE = "android.message"; /** + * @hide * Notification type: asynchronous bulk message (email). */ public static final String KIND_EMAIL = "android.email"; /** + * @hide * Notification type: calendar event. */ public static final String KIND_EVENT = "android.event"; @@ -415,6 +419,7 @@ public class Notification implements Parcelable public static final String KIND_PROMO = "android.promo"; /** + * @hide * If this notification matches of one or more special types (see the <code>KIND_*</code> * constants), add them here, best match first. */ @@ -796,6 +801,12 @@ public class Notification implements Parcelable if (this.icon != 0) { contentView.setImageViewResource(R.id.icon, this.icon); } + if (priority < PRIORITY_LOW) { + contentView.setInt(R.id.icon, + "setBackgroundResource", R.drawable.notification_template_icon_low_bg); + contentView.setInt(R.id.status_bar_latest_event_content, + "setBackgroundResource", R.drawable.notification_bg_low); + } if (contentTitle != null) { contentView.setTextViewText(R.id.title, contentTitle); } @@ -971,8 +982,14 @@ public class Notification implements Parcelable } /** - * Show the {@link Notification#when} field as a countdown (or count-up) timer instead of a timestamp. + * Show the {@link Notification#when} field as a stopwatch. + * + * Instead of presenting <code>when</code> as a timestamp, the notification will show an + * automatically updating display of the minutes and seconds since <code>when</code>. * + * Useful when showing an elapsed time (like an ongoing phone call). + * + * @see android.widget.Chronometer * @see Notification#when */ public Builder setUsesChronometer(boolean b) { @@ -1297,6 +1314,8 @@ public class Notification implements Parcelable } /** + * @hide + * * Add a kind (category) to this notification. Optional. * * @see Notification#kind @@ -1364,6 +1383,12 @@ public class Notification implements Parcelable contentView.setImageViewBitmap(R.id.icon, mLargeIcon); smallIconImageViewId = R.id.right_icon; } + if (mPriority < PRIORITY_LOW) { + contentView.setInt(R.id.icon, + "setBackgroundResource", R.drawable.notification_template_icon_low_bg); + contentView.setInt(R.id.status_bar_latest_event_content, + "setBackgroundResource", R.drawable.notification_bg_low); + } if (mSmallIcon != 0) { contentView.setImageViewResource(smallIconImageViewId, mSmallIcon); contentView.setViewVisibility(smallIconImageViewId, View.VISIBLE); @@ -1478,7 +1503,16 @@ public class Notification implements Parcelable RemoteViews button = new RemoteViews(mContext.getPackageName(), R.layout.notification_action); button.setTextViewCompoundDrawables(R.id.action0, action.icon, 0, 0, 0); button.setTextViewText(R.id.action0, action.title); - button.setOnClickPendingIntent(R.id.action0, action.actionIntent); + if (action.actionIntent != null) { + button.setOnClickPendingIntent(R.id.action0, action.actionIntent); + //button.setBoolean(R.id.action0, "setEnabled", true); + button.setFloat(R.id.button0, "setAlpha", 1.0f); + button.setBoolean(R.id.button0, "setClickable", true); + } else { + //button.setBoolean(R.id.action0, "setEnabled", false); + button.setFloat(R.id.button0, "setAlpha", 0.5f); + button.setBoolean(R.id.button0, "setClickable", false); + } button.setContentDescription(R.id.action0, action.title); return button; } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 4ed6f25..c791e47 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4573,7 +4573,7 @@ public class Intent implements Parcelable, Cloneable { * <p><em>Note: scheme matching in the Android framework is * case-sensitive, unlike the formal RFC. As a result, * you should always write your Uri with a lower case scheme, - * or use {@link Uri#normalize} or + * or use {@link Uri#normalizeScheme} or * {@link #setDataAndNormalize} * to ensure that the scheme is converted to lower case.</em> * @@ -4599,7 +4599,7 @@ public class Intent implements Parcelable, Cloneable { * previously set (for example, by {@link #setType}). * * <p>The data Uri is normalized using - * {@link android.net.Uri#normalize} before it is set, + * {@link android.net.Uri#normalizeScheme} before it is set, * so really this is just a convenience method for * <pre> * setData(data.normalize()) @@ -4612,10 +4612,10 @@ public class Intent implements Parcelable, Cloneable { * * @see #getData * @see #setType - * @see android.net.Uri#normalize + * @see android.net.Uri#normalizeScheme */ public Intent setDataAndNormalize(Uri data) { - return setData(data.normalize()); + return setData(data.normalizeScheme()); } /** @@ -4687,7 +4687,7 @@ public class Intent implements Parcelable, Cloneable { * <p><em>Note: MIME type and Uri scheme matching in the * Android framework is case-sensitive, unlike the formal RFC definitions. * As a result, you should always write these elements with lower case letters, - * or use {@link #normalizeMimeType} or {@link android.net.Uri#normalize} or + * or use {@link #normalizeMimeType} or {@link android.net.Uri#normalizeScheme} or * {@link #setDataAndTypeAndNormalize} * to ensure that they are converted to lower case.</em> * @@ -4700,7 +4700,7 @@ public class Intent implements Parcelable, Cloneable { * @see #setType * @see #setData * @see #normalizeMimeType - * @see android.net.Uri#normalize + * @see android.net.Uri#normalizeScheme * @see #setDataAndTypeAndNormalize */ public Intent setDataAndType(Uri data, String type) { @@ -4716,7 +4716,7 @@ public class Intent implements Parcelable, Cloneable { * data with your own type given here. * * <p>The data Uri and the MIME type are normalize using - * {@link android.net.Uri#normalize} and {@link #normalizeMimeType} + * {@link android.net.Uri#normalizeScheme} and {@link #normalizeMimeType} * before they are set, so really this is just a convenience method for * <pre> * setDataAndType(data.normalize(), Intent.normalizeMimeType(type)) @@ -4732,10 +4732,10 @@ public class Intent implements Parcelable, Cloneable { * @see #setData * @see #setDataAndType * @see #normalizeMimeType - * @see android.net.Uri#normalize + * @see android.net.Uri#normalizeScheme */ public Intent setDataAndTypeAndNormalize(Uri data, String type) { - return setDataAndType(data.normalize(), normalizeMimeType(type)); + return setDataAndType(data.normalizeScheme(), normalizeMimeType(type)); } /** diff --git a/core/java/android/content/pm/ContainerEncryptionParams.java b/core/java/android/content/pm/ContainerEncryptionParams.java index 5b1440d..88112a7 100644 --- a/core/java/android/content/pm/ContainerEncryptionParams.java +++ b/core/java/android/content/pm/ContainerEncryptionParams.java @@ -70,16 +70,16 @@ public class ContainerEncryptionParams implements Parcelable { private final byte[] mMacTag; /** Offset into file where authenticated (e.g., MAC protected) data begins. */ - private final int mAuthenticatedDataStart; + private final long mAuthenticatedDataStart; /** Offset into file where encrypted data begins. */ - private final int mEncryptedDataStart; + private final long mEncryptedDataStart; /** * Offset into file for the end of encrypted data (and, by extension, * authenticated data) in file. */ - private final int mDataEnd; + private final long mDataEnd; public ContainerEncryptionParams(String encryptionAlgorithm, AlgorithmParameterSpec encryptionSpec, SecretKey encryptionKey) @@ -99,6 +99,8 @@ public class ContainerEncryptionParams implements Parcelable { * @param macAlgorithm MAC algorithm to use; format matches JCE * @param macSpec algorithm parameters specification, may be {@code null} * @param macKey key used for authentication (i.e., for the MAC tag) + * @param macTag message authentication code (MAC) tag for the authenticated + * data * @param authenticatedDataStart offset of start of authenticated data in * stream * @param encryptedDataStart offset of start of encrypted data in stream @@ -109,7 +111,7 @@ public class ContainerEncryptionParams implements Parcelable { public ContainerEncryptionParams(String encryptionAlgorithm, AlgorithmParameterSpec encryptionSpec, SecretKey encryptionKey, String macAlgorithm, AlgorithmParameterSpec macSpec, SecretKey macKey, byte[] macTag, - int authenticatedDataStart, int encryptedDataStart, int dataEnd) + long authenticatedDataStart, long encryptedDataStart, long dataEnd) throws InvalidAlgorithmParameterException { if (TextUtils.isEmpty(encryptionAlgorithm)) { throw new NullPointerException("algorithm == null"); @@ -172,15 +174,15 @@ public class ContainerEncryptionParams implements Parcelable { return mMacTag; } - public int getAuthenticatedDataStart() { + public long getAuthenticatedDataStart() { return mAuthenticatedDataStart; } - public int getEncryptedDataStart() { + public long getEncryptedDataStart() { return mEncryptedDataStart; } - public int getDataEnd() { + public long getDataEnd() { return mDataEnd; } @@ -315,9 +317,9 @@ public class ContainerEncryptionParams implements Parcelable { dest.writeByteArray(mMacTag); - dest.writeInt(mAuthenticatedDataStart); - dest.writeInt(mEncryptedDataStart); - dest.writeInt(mDataEnd); + dest.writeLong(mAuthenticatedDataStart); + dest.writeLong(mEncryptedDataStart); + dest.writeLong(mDataEnd); } private ContainerEncryptionParams(Parcel source) throws InvalidAlgorithmParameterException { @@ -333,9 +335,9 @@ public class ContainerEncryptionParams implements Parcelable { mMacTag = source.createByteArray(); - mAuthenticatedDataStart = source.readInt(); - mEncryptedDataStart = source.readInt(); - mDataEnd = source.readInt(); + mAuthenticatedDataStart = source.readLong(); + mEncryptedDataStart = source.readLong(); + mDataEnd = source.readLong(); switch (encParamType) { case ENC_PARAMS_IV_PARAMETERS: diff --git a/core/java/android/content/pm/LimitedLengthInputStream.java b/core/java/android/content/pm/LimitedLengthInputStream.java index 25a490f..e787277 100644 --- a/core/java/android/content/pm/LimitedLengthInputStream.java +++ b/core/java/android/content/pm/LimitedLengthInputStream.java @@ -3,6 +3,7 @@ package android.content.pm; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; /** * A class that limits the amount of data that is read from an InputStream. When @@ -15,20 +16,20 @@ public class LimitedLengthInputStream extends FilterInputStream { /** * The end of the stream where we don't want to allow more data to be read. */ - private final int mEnd; + private final long mEnd; /** * Current offset in the stream. */ - private int mOffset; + private long mOffset; /** * @param in underlying stream to wrap * @param offset offset into stream where data starts * @param length length of data at offset - * @throws IOException if an error occured with the underlying stream + * @throws IOException if an error occurred with the underlying stream */ - public LimitedLengthInputStream(InputStream in, int offset, int length) throws IOException { + public LimitedLengthInputStream(InputStream in, long offset, long length) throws IOException { super(in); if (in == null) { @@ -36,11 +37,15 @@ public class LimitedLengthInputStream extends FilterInputStream { } if (offset < 0) { - throw new IOException("offset == " + offset); + throw new IOException("offset < 0"); } if (length < 0) { - throw new IOException("length must be non-negative; is " + length); + throw new IOException("length < 0"); + } + + if (length > Long.MAX_VALUE - offset) { + throw new IOException("offset + length > Long.MAX_VALUE"); } mEnd = offset + length; @@ -65,8 +70,15 @@ public class LimitedLengthInputStream extends FilterInputStream { return -1; } + final int arrayLength = buffer.length; + Arrays.checkOffsetAndCount(arrayLength, offset, byteCount); + + if (mOffset > Long.MAX_VALUE - byteCount) { + throw new IOException("offset out of bounds: " + mOffset + " + " + byteCount); + } + if (mOffset + byteCount > mEnd) { - byteCount = mEnd - mOffset; + byteCount = (int) (mEnd - mOffset); } final int numRead = super.read(buffer, offset, byteCount); diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index defe7aa..3b990e3 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -1718,9 +1718,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { } /** - * Return a normalized representation of this Uri. - * - * <p>A normalized Uri has a lowercase scheme component. + * Return an equivalent URI with a lowercase scheme component. * This aligns the Uri with Android best practices for * intent filtering. * @@ -1740,7 +1738,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * @see {@link android.content.Intent#setData} * @see {@link #setNormalizedData} */ - public Uri normalize() { + public Uri normalizeScheme() { String scheme = getScheme(); if (scheme == null) return this; // give up String lowerScheme = scheme.toLowerCase(Locale.US); diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java index 0e9e8f4..8872335 100644 --- a/core/java/android/nfc/NdefRecord.java +++ b/core/java/android/nfc/NdefRecord.java @@ -321,7 +321,7 @@ public final class NdefRecord implements Parcelable { * and {@link #RTD_URI}. This is the most efficient encoding * of a URI into NDEF.<p> * The uri parameter will be normalized with - * {@link Uri#normalize} to set the scheme to lower case to + * {@link Uri#normalizeScheme} to set the scheme to lower case to * follow Android best practices for intent filtering. * However the unchecked exception * {@link IllegalArgumentException} may be thrown if the uri @@ -338,7 +338,7 @@ public final class NdefRecord implements Parcelable { public static NdefRecord createUri(Uri uri) { if (uri == null) throw new NullPointerException("uri is null"); - uri = uri.normalize(); + uri = uri.normalizeScheme(); String uriString = uri.toString(); if (uriString.length() == 0) throw new IllegalArgumentException("uri is empty"); @@ -364,7 +364,7 @@ public final class NdefRecord implements Parcelable { * and {@link #RTD_URI}. This is the most efficient encoding * of a URI into NDEF.<p> * The uriString parameter will be normalized with - * {@link Uri#normalize} to set the scheme to lower case to + * {@link Uri#normalizeScheme} to set the scheme to lower case to * follow Android best practices for intent filtering. * However the unchecked exception * {@link IllegalArgumentException} may be thrown if the uriString @@ -665,7 +665,7 @@ public final class NdefRecord implements Parcelable { * actually valid: it always attempts to create and return a URI if * this record appears to be a URI record by the above rules.<p> * The returned URI will be normalized to have a lower case scheme - * using {@link Uri#normalize}.<p> + * using {@link Uri#normalizeScheme}.<p> * * @return URI, or null if this is not a URI record */ @@ -688,13 +688,13 @@ public final class NdefRecord implements Parcelable { } } catch (FormatException e) { } } else if (Arrays.equals(mType, RTD_URI)) { - return parseWktUri().normalize(); + return parseWktUri().normalizeScheme(); } break; case TNF_ABSOLUTE_URI: Uri uri = Uri.parse(new String(mType, Charsets.UTF_8)); - return uri.normalize(); + return uri.normalizeScheme(); case TNF_EXTERNAL_TYPE: if (inSmartPoster) { diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 63275cf..2d5b625 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -370,7 +370,7 @@ public class Build { public static final int ICE_CREAM_SANDWICH_MR1 = 15; /** - * Next up on Android! + * Android 4.1. * * <p>Applications targeting this or a later release will get these * new changes in behavior:</p> @@ -381,7 +381,7 @@ public class Build { * exist in the application's manifest. * </ul> */ - public static final int JELLY_BEAN = CUR_DEVELOPMENT; + public static final int JELLY_BEAN = 16; } /** The type of build, like "user" or "eng". */ diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java index 5fd7c57..ecbf4bc 100644 --- a/core/java/android/text/style/TextAppearanceSpan.java +++ b/core/java/android/text/style/TextAppearanceSpan.java @@ -68,24 +68,29 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl TextAppearance_textSize, -1); mStyle = a.getInt(com.android.internal.R.styleable.TextAppearance_textStyle, 0); - int tf = a.getInt(com.android.internal.R.styleable.TextAppearance_typeface, 0); - - switch (tf) { - case 1: - mTypeface = "sans"; - break; - - case 2: - mTypeface = "serif"; - break; - - case 3: - mTypeface = "monospace"; - break; - - default: - mTypeface = null; - break; + String family = a.getString(com.android.internal.R.styleable.TextAppearance_fontFamily); + if (family != null) { + mTypeface = family; + } else { + int tf = a.getInt(com.android.internal.R.styleable.TextAppearance_typeface, 0); + + switch (tf) { + case 1: + mTypeface = "sans"; + break; + + case 2: + mTypeface = "serif"; + break; + + case 3: + mTypeface = "monospace"; + break; + + default: + mTypeface = null; + break; + } } a.recycle(); diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java index 2883eca..c4ebec4 100644 --- a/core/java/android/util/TimeUtils.java +++ b/core/java/android/util/TimeUtils.java @@ -26,6 +26,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Calendar; import java.util.Collection; import java.util.TimeZone; import java.util.Date; @@ -381,4 +382,22 @@ public class TimeUtils { } formatDuration(time-now, pw, 0); } + + /** + * Convert a System.currentTimeMillis() value to a time of day value like + * that printed in logs. MM-DD HH:MM:SS.MMM + * + * @param millis since the epoch (1/1/1970) + * @return String representation of the time. + * @hide + */ + public static String logTimeOfDay(long millis) { + Calendar c = Calendar.getInstance(); + if (millis >= 0) { + c.setTimeInMillis(millis); + return String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c); + } else { + return Long.toString(millis); + } + } } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index e1f01db..c5a687a 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -84,7 +84,7 @@ interface IWindowManager void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth, int startHeight); void overridePendingAppTransitionThumb(in Bitmap srcThumb, int startX, int startY, - IRemoteCallback startedCallback); + IRemoteCallback startedCallback, boolean delayed); void executeAppTransition(); void setAppStartingWindow(IBinder token, String pkg, int theme, in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index aad6756..bf7d037 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5209,6 +5209,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * call to continue to your children, you must be sure to call the super * implementation. * + * <p>Here is a sample layout that makes use of fitting system windows + * to have controls for a video view placed inside of the window decorations + * that it hides and shows. This can be used with code like the second + * sample (video player) shown in {@link #setSystemUiVisibility(int)}. + * + * {@sample development/samples/ApiDemos/res/layout/video_player.xml complete} + * * @param insets Current content insets of the window. Prior to * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} you must not modify * the insets or else you and Android will be unhappy. @@ -5251,7 +5258,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } /** - * Check for the FITS_SYSTEM_WINDOWS flag. If this method returns true, this view + * Check for state of {@link #setFitsSystemWindows(boolean). If this method + * returns true, this view * will account for system screen decorations such as the status bar and inset its * content. This allows the view to be positioned in absolute screen coordinates * and remain visible to the user. @@ -5260,10 +5268,15 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * * @attr ref android.R.styleable#View_fitsSystemWindows */ - public boolean fitsSystemWindows() { + public boolean getFitsSystemWindows() { return (mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS; } + /** @hide */ + public boolean fitsSystemWindows() { + return getFitsSystemWindows(); + } + /** * Ask that a new dispatch of {@link #fitSystemWindows(Rect)} be performed. */ @@ -15376,7 +15389,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * playing the application would like to go into a complete full-screen mode, * to use as much of the display as possible for the video. When in this state * the user can not interact with the application; the system intercepts - * touching on the screen to pop the UI out of full screen mode. + * touching on the screen to pop the UI out of full screen mode. See + * {@link #fitSystemWindows(Rect)} for a sample layout that goes with this code. * * {@sample development/samples/ApiDemos/src/com/example/android/apis/view/VideoPlayerActivity.java * content} @@ -15458,11 +15472,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } } - void updateLocalSystemUiVisibility(int localValue, int localChanges) { + boolean updateLocalSystemUiVisibility(int localValue, int localChanges) { int val = (mSystemUiVisibility&~localChanges) | (localValue&localChanges); if (val != mSystemUiVisibility) { setSystemUiVisibility(val); + return true; } + return false; } /** @hide */ @@ -16861,7 +16877,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Interface definition for a callback to be invoked when the status bar changes * visibility. This reports <strong>global</strong> changes to the system UI - * state, not just what the application is requesting. + * state, not what the application is requesting. * * @see View#setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener) */ @@ -16870,10 +16886,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Called when the status bar changes visibility because of a call to * {@link View#setSystemUiVisibility(int)}. * - * @param visibility Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE} or - * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}. This tells you the - * <strong>global</strong> state of the UI visibility flags, not what your - * app is currently applying. + * @param visibility Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE}, + * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, and {@link #SYSTEM_UI_FLAG_FULLSCREEN}. + * This tells you the <strong>global</strong> state of these UI visibility + * flags, not what your app is currently applying. */ public void onSystemUiVisibilityChange(int visibility); } @@ -17159,6 +17175,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal int mDisabledSystemUiVisibility; /** + * Last global system UI visibility reported by the window manager. + */ + int mGlobalSystemUiVisibility; + + /** * True if a view in this hierarchy has an OnSystemUiVisibilityChangeListener * attached. */ diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index acfca26..b3c8895 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1317,15 +1317,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override - void updateLocalSystemUiVisibility(int localValue, int localChanges) { - super.updateLocalSystemUiVisibility(localValue, localChanges); + boolean updateLocalSystemUiVisibility(int localValue, int localChanges) { + boolean changed = super.updateLocalSystemUiVisibility(localValue, localChanges); final int count = mChildrenCount; final View[] children = mChildren; for (int i=0; i <count; i++) { final View child = children[i]; - child.updateLocalSystemUiVisibility(localValue, localChanges); + changed |= child.updateLocalSystemUiVisibility(localValue, localChanges); } + return changed; } /** diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index b43db14..1fcb2c3 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3795,13 +3795,15 @@ public final class ViewRootImpl implements ViewParent, } if (mView == null) return; if (args.localChanges != 0) { - if (mAttachInfo != null) { - mAttachInfo.mRecomputeGlobalAttributes = true; - } mView.updateLocalSystemUiVisibility(args.localValue, args.localChanges); - scheduleTraversals(); } - mView.dispatchSystemUiVisibilityChanged(args.globalVisibility); + if (mAttachInfo != null) { + int visibility = args.globalVisibility&View.SYSTEM_UI_CLEARABLE_FLAGS; + if (visibility != mAttachInfo.mGlobalSystemUiVisibility) { + mAttachInfo.mGlobalSystemUiVisibility = visibility; + mView.dispatchSystemUiVisibilityChanged(visibility); + } + } } public void handleDispatchDoneAnimating() { diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 6b14ba5..0517d4b 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -205,6 +205,16 @@ public class AccessibilityNodeInfo implements Parcelable { public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800; /** + * Action to scroll the node content forward. + */ + public static final int ACTION_SCROLL_FORWARD = 0x00001000; + + /** + * Action to scroll the node content backward. + */ + public static final int ACTION_SCROLL_BACKWARD = 0x00002000; + + /** * Argument for which movement granularity to be used when traversing the node text. * <p> * <strong>Type:</strong> int<br> @@ -569,6 +579,16 @@ public class AccessibilityNodeInfo implements Parcelable { * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS * @see AccessibilityNodeInfo#ACTION_SELECT * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION + * @see AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS + * @see AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS + * @see AccessibilityNodeInfo#ACTION_CLICK + * @see AccessibilityNodeInfo#ACTION_LONG_CLICK + * @see AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY + * @see AccessibilityNodeInfo#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY + * @see AccessibilityNodeInfo#ACTION_NEXT_HTML_ELEMENT + * @see AccessibilityNodeInfo#ACTION_PREVIOUS_HTML_ELEMENT + * @see AccessibilityNodeInfo#ACTION_SCROLL_FORWARD + * @see AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD */ public int getActions() { return mActions; @@ -1578,6 +1598,10 @@ public class AccessibilityNodeInfo implements Parcelable { return "ACTION_NEXT_HTML_ELEMENT"; case ACTION_PREVIOUS_HTML_ELEMENT: return "ACTION_PREVIOUS_HTML_ELEMENT"; + case ACTION_SCROLL_FORWARD: + return "ACTION_SCROLL_FORWARD"; + case ACTION_SCROLL_BACKWARD: + return "ACTION_SCROLL_BACKWARD"; default: throw new IllegalArgumentException("Unknown action: " + action); } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 3aafba5..c4e1bf5 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -1355,6 +1355,33 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(AbsListView.class.getName()); + if (getFirstVisiblePosition() > 0) { + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); + } + if (getLastVisiblePosition() < getCount() - 1) { + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); + } + } + + @Override + public boolean performAccessibilityAction(int action, Bundle arguments) { + switch (action) { + case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: { + if (getLastVisiblePosition() < getCount() - 1) { + final int viewportHeight = getHeight() - mListPadding.top - mListPadding.bottom; + smoothScrollBy(viewportHeight, PositionScroller.SCROLL_DURATION); + return true; + } + } return false; + case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: { + if (mFirstPosition > 0) { + final int viewportHeight = getHeight() - mListPadding.top - mListPadding.bottom; + smoothScrollBy(-viewportHeight, PositionScroller.SCROLL_DURATION); + return true; + } + } return false; + } + return super.performAccessibilityAction(action, arguments); } /** diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 1986450..ffabd1d 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; +import android.os.Bundle; import android.util.AttributeSet; import android.view.FocusFinder; import android.view.InputDevice; @@ -737,10 +738,42 @@ public class HorizontalScrollView extends FrameLayout { } @Override + public boolean performAccessibilityAction(int action, Bundle arguments) { + switch (action) { + case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: { + final int viewportWidth = getWidth() - mPaddingLeft - mPaddingRight; + final int targetScrollX = Math.min(mScrollX + viewportWidth, getScrollRange()); + if (targetScrollX != mScrollX) { + smoothScrollTo(targetScrollX, 0); + return true; + } + } return false; + case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: { + final int viewportWidth = getWidth() - mPaddingLeft - mPaddingRight; + final int targetScrollX = Math.max(0, mScrollX - viewportWidth); + if (targetScrollX != mScrollX) { + smoothScrollTo(targetScrollX, 0); + return true; + } + } return false; + } + return super.performAccessibilityAction(action, arguments); + } + + @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(HorizontalScrollView.class.getName()); - info.setScrollable(getScrollRange() > 0); + final int scrollRange = getScrollRange(); + if (scrollRange > 0) { + info.setScrollable(true); + if (mScrollX > 0) { + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); + } + if (mScrollX < scrollRange) { + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); + } + } } @Override diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 11d1ed0..6ff924b 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -133,16 +133,6 @@ public class NumberPicker extends LinearLayout { private static final int UNSCALED_DEFAULT_SELECTION_DIVIDERS_DISTANCE = 48; /** - * The default unscaled minimal distance for a swipe to be considered a fling. - */ - private static final int UNSCALED_DEFAULT_MIN_FLING_DISTANCE = 150; - - /** - * Coefficient for adjusting touch scroll distance. - */ - private static final float TOUCH_SCROLL_DECELERATION_COEFFICIENT = 2.0f; - - /** * The resource id for the default layout. */ private static final int DEFAULT_LAYOUT_RESOURCE_ID = R.layout.number_picker; @@ -233,11 +223,6 @@ public class NumberPicker extends LinearLayout { private final int mTextSize; /** - * The minimal distance for a swipe to be considered a fling. - */ - private final int mMinFlingDistance; - - /** * The height of the gap between text elements if the selector wheel. */ private int mSelectorTextGapHeight; @@ -298,6 +283,11 @@ public class NumberPicker extends LinearLayout { private final Paint mSelectorWheelPaint; /** + * The {@link Drawable} for pressed virtual (increment/decrement) buttons. + */ + private final Drawable mVirtualButtonPressedDrawable; + + /** * The height of a selector element (text + gap). */ private int mSelectorElementHeight; @@ -435,11 +425,26 @@ public class NumberPicker extends LinearLayout { private int mLastHoveredChildVirtualViewId; /** + * Whether the increment virtual button is pressed. + */ + private boolean mIncrementVirtualButtonPressed; + + /** + * Whether the decrement virtual button is pressed. + */ + private boolean mDecrementVirtualButtonPressed; + + /** * Provider to report to clients the semantic structure of this widget. */ private AccessibilityNodeProviderImpl mAccessibilityNodeProvider; /** + * Helper class for managing pressed state of the virtual buttons. + */ + private final PressedStateHelper mPressedStateHelper; + + /** * Interface to listen for changes of the current value. */ public interface OnValueChangeListener { @@ -553,12 +558,6 @@ public class NumberPicker extends LinearLayout { mSelectionDividersDistance = attributesArray.getDimensionPixelSize( R.styleable.NumberPicker_selectionDividersDistance, defSelectionDividerDistance); - final int defMinFlingDistance = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_MIN_FLING_DISTANCE, - getResources().getDisplayMetrics()); - mMinFlingDistance = attributesArray.getDimensionPixelSize( - R.styleable.NumberPicker_minFlingDistance, defMinFlingDistance); - mMinHeight = attributesArray.getDimensionPixelSize( R.styleable.NumberPicker_internalMinHeight, SIZE_UNSPECIFIED); @@ -581,8 +580,13 @@ public class NumberPicker extends LinearLayout { mComputeMaxWidth = (mMaxWidth == SIZE_UNSPECIFIED); + mVirtualButtonPressedDrawable = attributesArray.getDrawable( + R.styleable.NumberPicker_virtualButtonPressedDrawable); + attributesArray.recycle(); + mPressedStateHelper = new PressedStateHelper(); + // By default Linearlayout that we extend is not drawn. This is // its draw() method is not called but dispatchDraw() is called // directly (see ViewGroup.drawChild()). However, this class uses @@ -776,7 +780,19 @@ public class NumberPicker extends LinearLayout { mLastDownEventTime = event.getEventTime(); mIngonreMoveEvents = false; mShowSoftInputOnTap = false; - // Make sure we wupport flinging inside scrollables. + // Handle pressed state before any state change. + if (mLastDownEventY < mTopSelectionDividerTop) { + if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) { + mPressedStateHelper.buttonPressDelayed( + PressedStateHelper.BUTTON_DECREMENT); + } + } else if (mLastDownEventY > mBottomSelectionDividerBottom) { + if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) { + mPressedStateHelper.buttonPressDelayed( + PressedStateHelper.BUTTON_INCREMENT); + } + } + // Make sure we support flinging inside scrollables. getParent().requestDisallowInterceptTouchEvent(true); if (!mFlingScroller.isFinished()) { mFlingScroller.forceFinished(true); @@ -826,8 +842,7 @@ public class NumberPicker extends LinearLayout { onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); } } else { - int deltaMoveY = (int) ((currentMoveY - mLastDownOrMoveEventY) - / TOUCH_SCROLL_DECELERATION_COEFFICIENT); + int deltaMoveY = (int) ((currentMoveY - mLastDownOrMoveEventY)); scrollBy(0, deltaMoveY); invalidate(); } @@ -836,23 +851,12 @@ public class NumberPicker extends LinearLayout { case MotionEvent.ACTION_UP: { removeBeginSoftInputCommand(); removeChangeCurrentByOneFromLongPress(); + mPressedStateHelper.cancel(); VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); int initialVelocity = (int) velocityTracker.getYVelocity(); if (Math.abs(initialVelocity) > mMinimumFlingVelocity) { - int deltaMove = (int) (event.getY() - mLastDownEventY); - int absDeltaMoveY = Math.abs(deltaMove); - if (absDeltaMoveY > mMinFlingDistance) { - fling(initialVelocity); - } else { - final int normalizedDeltaMove = - (int) (absDeltaMoveY / TOUCH_SCROLL_DECELERATION_COEFFICIENT); - if (normalizedDeltaMove < mSelectorElementHeight) { - snapToNextValue(deltaMove < 0); - } else { - snapToClosestValue(); - } - } + fling(initialVelocity); onScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); } else { int eventY = (int) event.getY(); @@ -867,8 +871,12 @@ public class NumberPicker extends LinearLayout { - SELECTOR_MIDDLE_ITEM_INDEX; if (selectorIndexOffset > 0) { changeValueByOne(true); + mPressedStateHelper.buttonTapped( + PressedStateHelper.BUTTON_INCREMENT); } else if (selectorIndexOffset < 0) { changeValueByOne(false); + mPressedStateHelper.buttonTapped( + PressedStateHelper.BUTTON_DECREMENT); } } } else { @@ -1356,6 +1364,22 @@ public class NumberPicker extends LinearLayout { float x = (mRight - mLeft) / 2; float y = mCurrentScrollOffset; + // draw the virtual buttons pressed state if needed + if (mVirtualButtonPressedDrawable != null + && mScrollState == OnScrollListener.SCROLL_STATE_IDLE) { + if (mDecrementVirtualButtonPressed) { + mVirtualButtonPressedDrawable.setState(PRESSED_STATE_SET); + mVirtualButtonPressedDrawable.setBounds(0, 0, mRight, mTopSelectionDividerTop); + mVirtualButtonPressedDrawable.draw(canvas); + } + if (mIncrementVirtualButtonPressed) { + mVirtualButtonPressedDrawable.setState(PRESSED_STATE_SET); + mVirtualButtonPressedDrawable.setBounds(0, mBottomSelectionDividerBottom, mRight, + mBottom); + mVirtualButtonPressedDrawable.draw(canvas); + } + } + // draw the selector wheel int[] selectorIndices = mSelectorIndices; for (int i = 0; i < selectorIndices.length; i++) { @@ -1465,15 +1489,15 @@ public class NumberPicker extends LinearLayout { */ private void initializeSelectorWheelIndices() { mSelectorIndexToStringCache.clear(); - int[] selectorIdices = mSelectorIndices; + int[] selectorIndices = mSelectorIndices; int current = getValue(); for (int i = 0; i < mSelectorIndices.length; i++) { int selectorIndex = current + (i - SELECTOR_MIDDLE_ITEM_INDEX); if (mWrapSelectorWheel) { selectorIndex = getWrappedSelectorIndex(selectorIndex); } - mSelectorIndices[i] = selectorIndex; - ensureCachedScrollSelectorValue(mSelectorIndices[i]); + selectorIndices[i] = selectorIndex; + ensureCachedScrollSelectorValue(selectorIndices[i]); } } @@ -1775,6 +1799,7 @@ public class NumberPicker extends LinearLayout { if (mBeginSoftInputOnLongPressCommand != null) { removeCallbacks(mBeginSoftInputOnLongPressCommand); } + mPressedStateHelper.cancel(); } /** @@ -1910,39 +1935,80 @@ public class NumberPicker extends LinearLayout { return false; } - private void snapToNextValue(boolean increment) { - int deltaY = mCurrentScrollOffset - mInitialScrollOffset; - int amountToScroll = 0; - if (deltaY != 0) { - mPreviousScrollerY = 0; - if (deltaY > 0) { - if (increment) { - amountToScroll = - deltaY; - } else { - amountToScroll = mSelectorElementHeight - deltaY; - } - } else { - if (increment) { - amountToScroll = - mSelectorElementHeight - deltaY; - } else { - amountToScroll = - deltaY; - } + class PressedStateHelper implements Runnable { + public static final int BUTTON_INCREMENT = 1; + public static final int BUTTON_DECREMENT = 2; + + private final int MODE_PRESS = 1; + private final int MODE_TAPPED = 2; + + private int mManagedButton; + private int mMode; + + public void cancel() { + mMode = 0; + mManagedButton = 0; + NumberPicker.this.removeCallbacks(this); + if (mIncrementVirtualButtonPressed) { + mIncrementVirtualButtonPressed = false; + invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom); + } + mDecrementVirtualButtonPressed = false; + if (mDecrementVirtualButtonPressed) { + invalidate(0, 0, mRight, mTopSelectionDividerTop); } - mFlingScroller.startScroll(0, 0, 0, amountToScroll, SNAP_SCROLL_DURATION); - invalidate(); } - } - private void snapToClosestValue() { - // adjust to the closest value - int deltaY = mInitialScrollOffset - mCurrentScrollOffset; - if (deltaY != 0) { - mPreviousScrollerY = 0; - if (Math.abs(deltaY) > mSelectorElementHeight / 2) { - deltaY += (deltaY > 0) ? -mSelectorElementHeight : mSelectorElementHeight; + public void buttonPressDelayed(int button) { + cancel(); + mMode = MODE_PRESS; + mManagedButton = button; + NumberPicker.this.postDelayed(this, ViewConfiguration.getTapTimeout()); + } + + public void buttonTapped(int button) { + cancel(); + mMode = MODE_TAPPED; + mManagedButton = button; + NumberPicker.this.post(this); + } + + @Override + public void run() { + switch (mMode) { + case MODE_PRESS: { + switch (mManagedButton) { + case BUTTON_INCREMENT: { + mIncrementVirtualButtonPressed = true; + invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom); + } break; + case BUTTON_DECREMENT: { + mDecrementVirtualButtonPressed = true; + invalidate(0, 0, mRight, mTopSelectionDividerTop); + } + } + } break; + case MODE_TAPPED: { + switch (mManagedButton) { + case BUTTON_INCREMENT: { + if (!mIncrementVirtualButtonPressed) { + NumberPicker.this.postDelayed(this, + ViewConfiguration.getPressedStateDuration()); + } + mIncrementVirtualButtonPressed ^= true; + invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom); + } break; + case BUTTON_DECREMENT: { + if (!mDecrementVirtualButtonPressed) { + NumberPicker.this.postDelayed(this, + ViewConfiguration.getPressedStateDuration()); + } + mDecrementVirtualButtonPressed ^= true; + invalidate(0, 0, mRight, mTopSelectionDividerTop); + } + } + } break; } - mFlingScroller.startScroll(0, 0, 0, deltaY, SNAP_SCROLL_DURATION); - invalidate(); } } diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index f912c66..b398ce4 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; +import android.os.Bundle; import android.os.StrictMode; import android.util.AttributeSet; import android.view.FocusFinder; @@ -740,10 +741,42 @@ public class ScrollView extends FrameLayout { } @Override + public boolean performAccessibilityAction(int action, Bundle arguments) { + switch (action) { + case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: { + final int viewportHeight = getHeight() - mPaddingBottom - mPaddingTop; + final int targetScrollY = Math.min(mScrollY + viewportHeight, getScrollRange()); + if (targetScrollY != mScrollY) { + smoothScrollTo(0, targetScrollY); + return true; + } + } return false; + case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: { + final int viewportHeight = getHeight() - mPaddingBottom - mPaddingTop; + final int targetScrollY = Math.max(mScrollY - viewportHeight, 0); + if (targetScrollY != mScrollY) { + smoothScrollTo(0, targetScrollY); + return true; + } + } return false; + } + return super.performAccessibilityAction(action, arguments); + } + + @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(ScrollView.class.getName()); - info.setScrollable(getScrollRange() > 0); + final int scrollRange = getScrollRange(); + if (scrollRange > 0) { + info.setScrollable(true); + if (mScrollY > 0) { + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); + } + if (mScrollY < scrollRange) { + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); + } + } } @Override diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index ebf8a4a..7ca02e1 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -343,6 +343,36 @@ public class SpellChecker implements SpellCheckerSessionListener { if (!isInDictionary && looksLikeTypo) { createMisspelledSuggestionSpan( editable, suggestionsInfo, spellCheckSpan, offset, length); + } else { + // Valid word -- isInDictionary || !looksLikeTypo + if (mIsSentenceSpellCheckSupported) { + // Allow the spell checker to remove existing misspelled span by + // overwriting the span over the same place + final int spellCheckSpanStart = editable.getSpanStart(spellCheckSpan); + final int spellCheckSpanEnd = editable.getSpanEnd(spellCheckSpan); + final int start; + final int end; + if (offset != USE_SPAN_RANGE && length != USE_SPAN_RANGE) { + start = spellCheckSpanStart + offset; + end = start + length; + } else { + start = spellCheckSpanStart; + end = spellCheckSpanEnd; + } + if (spellCheckSpanStart >= 0 && spellCheckSpanEnd > spellCheckSpanStart + && end > start) { + final Long key = Long.valueOf(TextUtils.packRangeInLong(start, end)); + final SuggestionSpan tempSuggestionSpan = mSuggestionSpanCache.get(key); + if (tempSuggestionSpan != null) { + if (DBG) { + Log.i(TAG, "Remove existing misspelled span. " + + editable.subSequence(start, end)); + } + editable.removeSpan(tempSuggestionSpan); + mSuggestionSpanCache.remove(key); + } + } + } } return spellCheckSpan; } @@ -473,8 +503,16 @@ public class SpellChecker implements SpellCheckerSessionListener { private Object mRange = new Object(); public void parse(int start, int end) { - if (end > start) { - setRangeSpan((Editable) mTextView.getText(), start, end); + final int max = mTextView.length(); + final int parseEnd; + if (end > max) { + Log.w(TAG, "Parse invalid region, from " + start + " to " + end); + parseEnd = max; + } else { + parseEnd = end; + } + if (parseEnd > start) { + setRangeSpan((Editable) mTextView.getText(), start, parseEnd); parse(); } } @@ -612,6 +650,8 @@ public class SpellChecker implements SpellCheckerSessionListener { break; } if (spellCheckEnd <= spellCheckStart) { + Log.w(TAG, "Trying to spellcheck invalid region, from " + + start + " to " + end); break; } if (createSpellCheckSpan) { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 555c974..098a034 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -154,6 +154,7 @@ import java.util.Locale; * @attr ref android.R.styleable#TextView_textColorLink * @attr ref android.R.styleable#TextView_textSize * @attr ref android.R.styleable#TextView_textScaleX + * @attr ref android.R.styleable#TextView_fontFamily * @attr ref android.R.styleable#TextView_typeface * @attr ref android.R.styleable#TextView_textStyle * @attr ref android.R.styleable#TextView_cursorVisible @@ -464,6 +465,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener ColorStateList textColorHint = null; ColorStateList textColorLink = null; int textSize = 15; + String fontFamily = null; int typefaceIndex = -1; int styleIndex = -1; boolean allCaps = false; @@ -516,6 +518,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener typefaceIndex = appearance.getInt(attr, -1); break; + case com.android.internal.R.styleable.TextAppearance_fontFamily: + fontFamily = appearance.getString(attr); + break; + case com.android.internal.R.styleable.TextAppearance_textStyle: styleIndex = appearance.getInt(attr, -1); break; @@ -781,6 +787,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener styleIndex = a.getInt(attr, styleIndex); break; + case com.android.internal.R.styleable.TextView_fontFamily: + fontFamily = a.getString(attr); + break; + case com.android.internal.R.styleable.TextView_password: password = a.getBoolean(attr, password); break; @@ -1051,7 +1061,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener typefaceIndex = MONOSPACE; } - setTypefaceByIndex(typefaceIndex, styleIndex); + setTypefaceFromAttrs(fontFamily, typefaceIndex, styleIndex); if (shadowcolor != 0) { setShadowLayer(r, dx, dy, shadowcolor); @@ -1111,8 +1121,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - private void setTypefaceByIndex(int typefaceIndex, int styleIndex) { + private void setTypefaceFromAttrs(String familyName, int typefaceIndex, int styleIndex) { Typeface tf = null; + if (familyName != null) { + tf = Typeface.create(familyName, styleIndex); + if (tf != null) { + setTypeface(tf); + return; + } + } switch (typefaceIndex) { case SANS: tf = Typeface.SANS_SERIF; @@ -2160,14 +2177,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setLinkTextColor(colors); } + String familyName; int typefaceIndex, styleIndex; + familyName = appearance.getString(com.android.internal.R.styleable. + TextAppearance_fontFamily); typefaceIndex = appearance.getInt(com.android.internal.R.styleable. TextAppearance_typeface, -1); styleIndex = appearance.getInt(com.android.internal.R.styleable. TextAppearance_textStyle, -1); - setTypefaceByIndex(typefaceIndex, styleIndex); + setTypefaceFromAttrs(familyName, typefaceIndex, styleIndex); if (appearance.getBoolean(com.android.internal.R.styleable.TextAppearance_textAllCaps, false)) { @@ -2269,6 +2289,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @see #getTypeface() * + * @attr ref android.R.styleable#TextView_fontFamily * @attr ref android.R.styleable#TextView_typeface * @attr ref android.R.styleable#TextView_textStyle */ @@ -2290,6 +2311,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @see #setTypeface(Typeface) * + * @attr ref android.R.styleable#TextView_fontFamily * @attr ref android.R.styleable#TextView_typeface * @attr ref android.R.styleable#TextView_textStyle */ @@ -3690,15 +3712,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boolean forceUpdate = false; if (isPassword) { setTransformationMethod(PasswordTransformationMethod.getInstance()); - setTypefaceByIndex(MONOSPACE, 0); + setTypefaceFromAttrs(null /* fontFamily */, MONOSPACE, 0); } else if (isVisiblePassword) { if (mTransformation == PasswordTransformationMethod.getInstance()) { forceUpdate = true; } - setTypefaceByIndex(MONOSPACE, 0); + setTypefaceFromAttrs(null /* fontFamily */, MONOSPACE, 0); } else if (wasPassword || wasVisiblePassword) { // not in password mode, clean up typeface and transformation - setTypefaceByIndex(-1, -1); + setTypefaceFromAttrs(null /* fontFamily */, -1, -1); if (mTransformation == PasswordTransformationMethod.getInstance()) { forceUpdate = true; } |
