diff options
Diffstat (limited to 'core/java/android')
28 files changed, 864 insertions, 183 deletions
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index f3c6566..0df2949 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -1824,7 +1824,7 @@ public class AccountManager { * Returns an intent to an {@link Activity} that prompts the user to choose from a list of * accounts. * The caller will then typically start the activity by calling - * <code>startActivityWithResult(intent, ...);</code>. + * <code>startActivityForResult(intent, ...);</code>. * <p> * On success the activity returns a Bundle with the account name and type specified using * keys {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE}. diff --git a/core/java/android/speech/tts/SynthesisCallback.java b/core/java/android/speech/tts/SynthesisCallback.java index d70c371..f98bb09 100644 --- a/core/java/android/speech/tts/SynthesisCallback.java +++ b/core/java/android/speech/tts/SynthesisCallback.java @@ -22,10 +22,11 @@ package android.speech.tts; * {@link #start}, then {@link #audioAvailable} until all audio has been provided, then finally * {@link #done}. * - * * {@link #error} can be called at any stage in the synthesis process to * indicate that an error has occurred, but if the call is made after a call * to {@link #done}, it might be discarded. + * + * After {@link #start} been called, {@link #done} must be called regardless of errors. */ public interface SynthesisCallback { /** @@ -72,6 +73,8 @@ public interface SynthesisCallback { * This method should only be called on the synthesis thread, * while in {@link TextToSpeechService#onSynthesizeText}. * + * This method has to be called if {@link #start} was called. + * * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}. */ public int done(); diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 5e367cb..9d570fc 100755 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -793,8 +793,13 @@ public class TextToSpeech { } /** - * Speaks the string using the specified queuing strategy and speech - * parameters. + * Speaks the string using the specified queuing strategy and speech parameters. + * This method is asynchronous, i.e. the method just adds the request to the queue of TTS + * requests and then returns. The synthesis might not have finished (or even started!) at the + * time when this method returns. In order to reliably detect errors during synthesis, + * we recommend setting an utterance progress listener (see + * {@link #setOnUtteranceProgressListener}) and using the + * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. * * @param text The string of text to be spoken. * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. @@ -809,7 +814,7 @@ public class TextToSpeech { * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the * engine named "com.svox.pico" if it is being used. * - * @return {@link #ERROR} or {@link #SUCCESS}. + * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation. */ public int speak(final String text, final int queueMode, final HashMap<String, String> params) { return runAction(new Action<Integer>() { @@ -830,6 +835,12 @@ public class TextToSpeech { * Plays the earcon using the specified queueing mode and parameters. * The earcon must already have been added with {@link #addEarcon(String, String)} or * {@link #addEarcon(String, String, int)}. + * This method is asynchronous, i.e. the method just adds the request to the queue of TTS + * requests and then returns. The synthesis might not have finished (or even started!) at the + * time when this method returns. In order to reliably detect errors during synthesis, + * we recommend setting an utterance progress listener (see + * {@link #setOnUtteranceProgressListener}) and using the + * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. * * @param earcon The earcon that should be played * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. @@ -842,7 +853,7 @@ public class TextToSpeech { * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the * engine named "com.svox.pico" if it is being used. * - * @return {@link #ERROR} or {@link #SUCCESS}. + * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation. */ public int playEarcon(final String earcon, final int queueMode, final HashMap<String, String> params) { @@ -862,6 +873,12 @@ public class TextToSpeech { /** * Plays silence for the specified amount of time using the specified * queue mode. + * This method is asynchronous, i.e. the method just adds the request to the queue of TTS + * requests and then returns. The synthesis might not have finished (or even started!) at the + * time when this method returns. In order to reliably detect errors during synthesis, + * we recommend setting an utterance progress listener (see + * {@link #setOnUtteranceProgressListener}) and using the + * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. * * @param durationInMs The duration of the silence. * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. @@ -873,7 +890,7 @@ public class TextToSpeech { * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the * engine named "com.svox.pico" if it is being used. * - * @return {@link #ERROR} or {@link #SUCCESS}. + * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation. */ public int playSilence(final long durationInMs, final int queueMode, final HashMap<String, String> params) { @@ -1089,6 +1106,12 @@ public class TextToSpeech { /** * Synthesizes the given text to a file using the specified parameters. + * This method is asynchronous, i.e. the method just adds the request to the queue of TTS + * requests and then returns. The synthesis might not have finished (or even started!) at the + * time when this method returns. In order to reliably detect errors during synthesis, + * we recommend setting an utterance progress listener (see + * {@link #setOnUtteranceProgressListener}) and using the + * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. * * @param text The text that should be synthesized * @param params Parameters for the request. Can be null. @@ -1101,7 +1124,7 @@ public class TextToSpeech { * @param filename Absolute file filename to write the generated audio data to.It should be * something like "/sdcard/myappsounds/mysound.wav". * - * @return {@link #ERROR} or {@link #SUCCESS}. + * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation. */ public int synthesizeToFile(final String text, final HashMap<String, String> params, final String filename) { diff --git a/core/java/android/text/method/DigitsKeyListener.java b/core/java/android/text/method/DigitsKeyListener.java index 3d9daed..c95df46 100644 --- a/core/java/android/text/method/DigitsKeyListener.java +++ b/core/java/android/text/method/DigitsKeyListener.java @@ -49,13 +49,22 @@ public class DigitsKeyListener extends NumberKeyListener * @see KeyEvent#getMatch * @see #getAcceptedChars */ - private static final char[][] CHARACTERS = new char[][] { - new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }, - new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-' }, - new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.' }, - new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.' }, + private static final char[][] CHARACTERS = { + { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }, + { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '+' }, + { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.' }, + { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '+', '.' }, }; + private static boolean isSignChar(final char c) { + return c == '-' || c == '+'; + } + + // TODO: Needs internationalization + private static boolean isDecimalPointChar(final char c) { + return c == '.'; + } + /** * Allocates a DigitsKeyListener that accepts the digits 0 through 9. */ @@ -145,32 +154,32 @@ public class DigitsKeyListener extends NumberKeyListener int dlen = dest.length(); /* - * Find out if the existing text has '-' or '.' characters. + * Find out if the existing text has a sign or decimal point characters. */ for (int i = 0; i < dstart; i++) { char c = dest.charAt(i); - if (c == '-') { + if (isSignChar(c)) { sign = i; - } else if (c == '.') { + } else if (isDecimalPointChar(c)) { decimal = i; } } for (int i = dend; i < dlen; i++) { char c = dest.charAt(i); - if (c == '-') { - return ""; // Nothing can be inserted in front of a '-'. - } else if (c == '.') { + if (isSignChar(c)) { + return ""; // Nothing can be inserted in front of a sign character. + } else if (isDecimalPointChar(c)) { decimal = i; } } /* * If it does, we must strip them out from the source. - * In addition, '-' must be the very first character, - * and nothing can be inserted before an existing '-'. + * In addition, a sign character must be the very first character, + * and nothing can be inserted before an existing sign character. * Go in reverse order so the offsets are stable. */ @@ -180,7 +189,7 @@ public class DigitsKeyListener extends NumberKeyListener char c = source.charAt(i); boolean strip = false; - if (c == '-') { + if (isSignChar(c)) { if (i != start || dstart != 0) { strip = true; } else if (sign >= 0) { @@ -188,7 +197,7 @@ public class DigitsKeyListener extends NumberKeyListener } else { sign = i; } - } else if (c == '.') { + } else if (isDecimalPointChar(c)) { if (decimal >= 0) { strip = true; } else { diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java index 5dc206f..0ec7e84 100644 --- a/core/java/android/text/style/SuggestionSpan.java +++ b/core/java/android/text/style/SuggestionSpan.java @@ -17,6 +17,7 @@ package android.text.style; import android.content.Context; +import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Color; import android.os.Parcel; @@ -26,6 +27,7 @@ import android.text.ParcelableSpan; import android.text.TextPaint; import android.text.TextUtils; import android.util.Log; +import android.view.inputmethod.InputMethodManager; import android.widget.TextView; import java.util.Arrays; @@ -45,6 +47,8 @@ import java.util.Locale; */ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { + private static final String TAG = "SuggestionSpan"; + /** * Sets this flag if the suggestions should be easily accessible with few interactions. * This flag should be set for every suggestions that the user is likely to use. @@ -82,6 +86,7 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { private final String[] mSuggestions; private final String mLocaleString; private final String mNotificationTargetClassName; + private final String mNotificationTargetPackageName; private final int mHashCode; private float mEasyCorrectUnderlineThickness; @@ -134,6 +139,12 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { mLocaleString = ""; } + if (context != null) { + mNotificationTargetPackageName = context.getPackageName(); + } else { + mNotificationTargetPackageName = null; + } + if (notificationTargetClass != null) { mNotificationTargetClassName = notificationTargetClass.getCanonicalName(); } else { @@ -185,6 +196,7 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { mFlags = src.readInt(); mLocaleString = src.readString(); mNotificationTargetClassName = src.readString(); + mNotificationTargetPackageName = src.readString(); mHashCode = src.readInt(); mEasyCorrectUnderlineColor = src.readInt(); mEasyCorrectUnderlineThickness = src.readFloat(); @@ -240,6 +252,7 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { dest.writeInt(mFlags); dest.writeString(mLocaleString); dest.writeString(mNotificationTargetClassName); + dest.writeString(mNotificationTargetPackageName); dest.writeInt(mHashCode); dest.writeInt(mEasyCorrectUnderlineColor); dest.writeFloat(mEasyCorrectUnderlineThickness); @@ -325,4 +338,40 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { } return 0; } + + /** + * Notifies a suggestion selection. + * + * @hide + */ + public void notifySelection(Context context, String original, int index) { + final Intent intent = new Intent(); + + if (context == null || mNotificationTargetClassName == null) { + return; + } + // Ensures that only a class in the original IME package will receive the + // notification. + if (mSuggestions == null || index < 0 || index >= mSuggestions.length) { + Log.w(TAG, "Unable to notify the suggestion as the index is out of range index=" + index + + " length=" + mSuggestions.length); + return; + } + + // The package name is not mandatory (legacy from JB), and if the package name + // is missing, we try to notify the suggestion through the input method manager. + if (mNotificationTargetPackageName != null) { + intent.setClassName(mNotificationTargetPackageName, mNotificationTargetClassName); + intent.setAction(SuggestionSpan.ACTION_SUGGESTION_PICKED); + intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_BEFORE, original); + intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_AFTER, mSuggestions[index]); + intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_HASHCODE, hashCode()); + context.sendBroadcast(intent); + } else { + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + imm.notifySuggestionPicked(this, original, index); + } + } + } } diff --git a/core/java/android/util/PropertyValueModel.java b/core/java/android/util/PropertyValueModel.java new file mode 100755 index 0000000..eb9c47d --- /dev/null +++ b/core/java/android/util/PropertyValueModel.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2012 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.util; + +/** + * A value model for a {@link Property property} of a host object. This class can be used for + * both reflective and non-reflective property implementations. + * + * @param <H> the host type, where the host is the object that holds this property + * @param <T> the value type + * + * @see Property + * @see ValueModel + */ +public class PropertyValueModel<H, T> extends ValueModel<T> { + private final H mHost; + private final Property<H, T> mProperty; + + private PropertyValueModel(H host, Property<H, T> property) { + mProperty = property; + mHost = host; + } + + /** + * Returns the host. + * + * @return the host + */ + public H getHost() { + return mHost; + } + + /** + * Returns the property. + * + * @return the property + */ + public Property<H, T> getProperty() { + return mProperty; + } + + @Override + public Class<T> getType() { + return mProperty.getType(); + } + + @Override + public T get() { + return mProperty.get(mHost); + } + + @Override + public void set(T value) { + mProperty.set(mHost, value); + } + + /** + * Return an appropriate PropertyValueModel for this host and property. + * + * @param host the host + * @param property the property + * @return the value model + */ + public static <H, T> PropertyValueModel<H, T> of(H host, Property<H, T> property) { + return new PropertyValueModel<H, T>(host, property); + } + + /** + * Return a PropertyValueModel for this {@code host} and a + * reflective property, constructed from this {@code propertyType} and {@code propertyName}. + * + * @param host + * @param propertyType the property type + * @param propertyName the property name + * @return a value model with this host and a reflective property with this type and name + * + * @see Property#of + */ + public static <H, T> PropertyValueModel<H, T> of(H host, Class<T> propertyType, + String propertyName) { + return of(host, Property.of((Class<H>) host.getClass(), propertyType, propertyName)); + } + + private static Class getNullaryMethodReturnType(Class c, String name) { + try { + return c.getMethod(name).getReturnType(); + } catch (NoSuchMethodException e) { + return null; + } + } + + private static Class getFieldType(Class c, String name) { + try { + return c.getField(name).getType(); + } catch (NoSuchFieldException e) { + return null; + } + } + + private static String capitalize(String name) { + if (name.isEmpty()) { + return name; + } + return Character.toUpperCase(name.charAt(0)) + name.substring(1); + } + + /** + * Return a PropertyValueModel for this {@code host} and and {@code propertyName}. + * + * @param host the host + * @param propertyName the property name + * @return a value model with this host and a reflective property with this name + */ + public static PropertyValueModel of(Object host, String propertyName) { + Class clazz = host.getClass(); + String suffix = capitalize(propertyName); + Class propertyType = getNullaryMethodReturnType(clazz, "get" + suffix); + if (propertyType == null) { + propertyType = getNullaryMethodReturnType(clazz, "is" + suffix); + } + if (propertyType == null) { + propertyType = getFieldType(clazz, propertyName); + } + if (propertyType == null) { + throw new NoSuchPropertyException(propertyName); + } + return of(host, propertyType, propertyName); + } +} diff --git a/core/java/android/util/ValueModel.java b/core/java/android/util/ValueModel.java new file mode 100755 index 0000000..4789682 --- /dev/null +++ b/core/java/android/util/ValueModel.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012 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.util; + +/** + * A ValueModel is an abstraction for a 'slot' or place in memory in which a value + * may be stored and retrieved. A common implementation of ValueModel is a regular property of + * an object, whose value may be retrieved by calling the appropriate <em>getter</em> + * method and set by calling the corresponding <em>setter</em> method. + * + * @param <T> the value type + * + * @see PropertyValueModel + */ +public abstract class ValueModel<T> { + /** + * The empty model should be used in place of {@code null} to indicate that a + * model has not been set. The empty model has no value and does nothing when it is set. + */ + public static final ValueModel EMPTY = new ValueModel() { + @Override + public Class getType() { + return Object.class; + } + + @Override + public Object get() { + return null; + } + + @Override + public void set(Object value) { + + } + }; + + protected ValueModel() { + } + + /** + * Returns the type of this property. + * + * @return the property type + */ + public abstract Class<T> getType(); + + /** + * Returns the value of this property. + * + * @return the property value + */ + public abstract T get(); + + /** + * Sets the value of this property. + * + * @param value the new value for this property + */ + public abstract void set(T value); +}
\ No newline at end of file diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index b36db7f..79a545f 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -13951,6 +13951,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Return true if o is a ViewGroup that is laying out using optical bounds. + * @hide + */ + public static boolean isLayoutModeOptical(Object o) { + return o instanceof ViewGroup && ((ViewGroup) o).isLayoutModeOptical(); + } + + private boolean setOpticalFrame(int left, int top, int right, int bottom) { + Insets parentInsets = mParent instanceof View ? + ((View) mParent).getOpticalInsets() : Insets.NONE; + Insets childInsets = getOpticalInsets(); + return setFrame( + left + parentInsets.left - childInsets.left, + top + parentInsets.top - childInsets.top, + right + parentInsets.left + childInsets.right, + bottom + parentInsets.top + childInsets.bottom); + } + + /** * Assign a size and position to a view and all of its * descendants * @@ -13976,7 +13995,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int oldT = mTop; int oldB = mBottom; int oldR = mRight; - boolean changed = setFrame(l, t, r, b); + boolean changed = isLayoutModeOptical(mParent) ? + setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { onLayout(changed, l, t, r, b); mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED; @@ -14790,6 +14810,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return (mUserPaddingStart != UNDEFINED_PADDING || mUserPaddingEnd != UNDEFINED_PADDING); } + Insets computeOpticalInsets() { + return (mBackground == null) ? Insets.NONE : mBackground.getOpticalInsets(); + } + /** * @hide */ @@ -14813,19 +14837,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public Insets getOpticalInsets() { if (mLayoutInsets == null) { - mLayoutInsets = (mBackground == null) ? Insets.NONE : mBackground.getLayoutInsets(); + mLayoutInsets = computeOpticalInsets(); } return mLayoutInsets; } /** - * @hide - */ - public void setLayoutInsets(Insets layoutInsets) { - mLayoutInsets = layoutInsets; - } - - /** * Changes the selection state of this view. A view can be selected or not. * Note that selection is not the same as focus. Views are typically * selected in the context of an AdapterView like ListView or GridView; @@ -15432,11 +15449,34 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Returns whether the view hierarchy is currently undergoing a layout pass. This + * information is useful to avoid situations such as calling {@link #requestLayout()} during + * a layout pass. + * + * @return whether the view hierarchy is currently undergoing a layout pass + */ + public boolean isInLayout() { + ViewRootImpl viewRoot = getViewRootImpl(); + return (viewRoot != null && viewRoot.isInLayout()); + } + + /** * Call this when something has changed which has invalidated the * layout of this view. This will schedule a layout pass of the view - * tree. + * tree. This should not be called while the view hierarchy is currently in a layout + * pass ({@link #isInLayout()}. If layout is happening, the request may be honored at the + * end of the current layout pass (and then layout will run again) or after the current + * frame is drawn and the next layout occurs. + * + * <p>Subclasses which override this method should call the superclass method to + * handle possible request-during-layout errors correctly.</p> */ public void requestLayout() { + ViewRootImpl viewRoot = getViewRootImpl(); + if (viewRoot != null && viewRoot.isInLayout()) { + viewRoot.requestLayoutDuringLayout(this); + return; + } mPrivateFlags |= PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED; @@ -15476,6 +15516,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #onMeasure(int, int) */ public final void measure(int widthMeasureSpec, int heightMeasureSpec) { + boolean optical = isLayoutModeOptical(this); + if (optical != isLayoutModeOptical(mParent)) { + Insets insets = getOpticalInsets(); + int oWidth = insets.left + insets.right; + int oHeight = insets.top + insets.bottom; + widthMeasureSpec = MeasureSpec.adjust(widthMeasureSpec, optical ? -oWidth : oWidth); + heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight); + } if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT || widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec) { @@ -15567,6 +15615,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #MEASURED_STATE_TOO_SMALL}. */ protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { + boolean optical = isLayoutModeOptical(this); + if (optical != isLayoutModeOptical(mParent)) { + Insets insets = getOpticalInsets(); + int opticalWidth = insets.left + insets.right; + int opticalHeight = insets.top + insets.bottom; + + measuredWidth += optical ? opticalWidth : -opticalWidth; + measuredHeight += optical ? opticalHeight : -opticalHeight; + } mMeasuredWidth = measuredWidth; mMeasuredHeight = measuredHeight; @@ -17233,7 +17290,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return the measure specification based on size and mode */ public static int makeMeasureSpec(int size, int mode) { - return size + mode; + return (size & ~MODE_MASK) | (mode & MODE_MASK); } /** @@ -17258,6 +17315,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return (measureSpec & ~MODE_MASK); } + static int adjust(int measureSpec, int delta) { + return makeMeasureSpec(getSize(measureSpec + delta), getMode(measureSpec)); + } + /** * Returns a String representation of the specified measure * specification. diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index dabdf5a..146fe2d 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -23,9 +23,11 @@ import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.DashPathEffect; import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Paint; +import android.graphics.PathEffect; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; @@ -83,6 +85,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager private static final String TAG = "ViewGroup"; private static final boolean DBG = false; + /** @hide */ + public static boolean DEBUG_DRAW = false; /** * Views which have been hidden or removed which need to be animated on @@ -180,10 +184,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager }) protected int mGroupFlags; - /* - * The layout mode: either {@link #CLIP_BOUNDS} or {@link #OPTICAL_BOUNDS} + /** + * Either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}. */ - private int mLayoutMode = CLIP_BOUNDS; + private int mLayoutMode = DEFAULT_LAYOUT_MODE; /** * NOTE: If you change the flags below make sure to reflect the changes @@ -356,20 +360,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * This constant is a {@link #setLayoutMode(int) layoutMode}. * Clip bounds are the raw values of {@link #getLeft() left}, {@link #getTop() top}, * {@link #getRight() right} and {@link #getBottom() bottom}. - * - * @hide */ - public static final int CLIP_BOUNDS = 0; + public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; /** * This constant is a {@link #setLayoutMode(int) layoutMode}. * Optical bounds describe where a widget appears to be. They sit inside the clip * bounds which need to cover a larger area to allow other effects, * such as shadows and glows, to be drawn. - * - * @hide */ - public static final int OPTICAL_BOUNDS = 1; + public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; + + /** @hide */ + public static int DEFAULT_LAYOUT_MODE = LAYOUT_MODE_CLIP_BOUNDS; /** * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL @@ -434,7 +437,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } private boolean debugDraw() { - return mAttachInfo != null && mAttachInfo.mDebugLayout; + return DEBUG_DRAW || mAttachInfo != null && mAttachInfo.mDebugLayout; } private void initViewGroup() { @@ -504,6 +507,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager setLayoutTransition(new LayoutTransition()); } break; + case R.styleable.ViewGroup_layoutMode: + setLayoutMode(a.getInt(attr, DEFAULT_LAYOUT_MODE)); + break; } } @@ -2420,7 +2426,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager for (int i = 0; i < count; i++) { final View child = children[i]; child.dispatchAttachedToWindow(info, - visibility | (child.mViewFlags&VISIBILITY_MASK)); + visibility | (child.mViewFlags & VISIBILITY_MASK)); } } @@ -2682,20 +2688,89 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return b; } - private static void drawRect(Canvas canvas, int x1, int y1, int x2, int y2, int color) { - Paint paint = getDebugPaint(); - paint.setColor(color); + /** Return true if this ViewGroup is laying out using optical bounds. */ + boolean isLayoutModeOptical() { + return mLayoutMode == LAYOUT_MODE_OPTICAL_BOUNDS; + } + + Insets computeOpticalInsets() { + if (isLayoutModeOptical()) { + int left = 0; + int top = 0; + int right = 0; + int bottom = 0; + for (int i = 0; i < mChildrenCount; i++) { + View child = getChildAt(i); + if (child.getVisibility() == VISIBLE) { + Insets insets = child.getOpticalInsets(); + left = Math.max(left, insets.left); + top = Math.max(top, insets.top); + right = Math.max(right, insets.right); + bottom = Math.max(bottom, insets.bottom); + } + } + return Insets.of(left, top, right, bottom); + } else { + return Insets.NONE; + } + } + + private static void fillRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) { + if (x1 != x2 && y1 != y2) { + if (x1 > x2) { + int tmp = x1; x1 = x2; x2 = tmp; + } + if (y1 > y2) { + int tmp = y1; y1 = y2; y2 = tmp; + } + canvas.drawRect(x1, y1, x2, y2, paint); + } + } + + private static int sign(int x) { + return (x >= 0) ? 1 : -1; + } - canvas.drawLines(getDebugLines(x1, y1, x2, y2), paint); + private static void drawCorner(Canvas c, Paint paint, int x1, int y1, int dx, int dy, int lw) { + fillRect(c, paint, x1, y1, x1 + dx, y1 + lw * sign(dy)); + fillRect(c, paint, x1, y1, x1 + lw * sign(dx), y1 + dy); + } + + private int dipsToPixels(int dips) { + float scale = getContext().getResources().getDisplayMetrics().density; + return (int) (dips * scale + 0.5f); + } + + private void drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint, + int lineLength, int lineWidth) { + drawCorner(canvas, paint, x1, y1, lineLength, lineLength, lineWidth); + drawCorner(canvas, paint, x1, y2, lineLength, -lineLength, lineWidth); + drawCorner(canvas, paint, x2, y1, -lineLength, lineLength, lineWidth); + drawCorner(canvas, paint, x2, y2, -lineLength, -lineLength, lineWidth); + } + + private static void fillDifference(Canvas canvas, + int x2, int y2, int x3, int y3, + int dx1, int dy1, int dx2, int dy2, Paint paint) { + int x1 = x2 - dx1; + int y1 = y2 - dy1; + + int x4 = x3 + dx2; + int y4 = y3 + dy2; + + fillRect(canvas, paint, x1, y1, x4, y2); + fillRect(canvas, paint, x1, y2, x2, y3); + fillRect(canvas, paint, x3, y2, x4, y3); + fillRect(canvas, paint, x1, y3, x4, y4); } /** * @hide */ - protected void onDebugDrawMargins(Canvas canvas) { + protected void onDebugDrawMargins(Canvas canvas, Paint paint) { for (int i = 0; i < getChildCount(); i++) { View c = getChildAt(i); - c.getLayoutParams().onDebugDraw(c, canvas); + c.getLayoutParams().onDebugDraw(c, canvas, paint); } } @@ -2703,26 +2778,45 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @hide */ protected void onDebugDraw(Canvas canvas) { + Paint paint = getDebugPaint(); + // Draw optical bounds - if (getLayoutMode() == OPTICAL_BOUNDS) { + { + paint.setColor(Color.RED); + paint.setStyle(Paint.Style.STROKE); + for (int i = 0; i < getChildCount(); i++) { View c = getChildAt(i); Insets insets = c.getOpticalInsets(); - drawRect(canvas, - c.getLeft() + insets.left, - c.getTop() + insets.top, - c.getRight() - insets.right, - c.getBottom() - insets.bottom, Color.RED); + + drawRect(canvas, paint, + c.getLeft() + insets.left, + c.getTop() + insets.top, + c.getRight() - insets.right - 1, + c.getBottom() - insets.bottom - 1); } } // Draw margins - onDebugDrawMargins(canvas); + { + paint.setColor(Color.argb(63, 255, 0, 255)); + paint.setStyle(Paint.Style.FILL); - // Draw bounds - for (int i = 0; i < getChildCount(); i++) { - View c = getChildAt(i); - drawRect(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(), Color.BLUE); + onDebugDrawMargins(canvas, paint); + } + + // Draw clip bounds + { + paint.setColor(Color.rgb(63, 127, 255)); + paint.setStyle(Paint.Style.FILL); + + int lineLength = dipsToPixels(8); + int lineWidth = dipsToPixels(1); + for (int i = 0; i < getChildCount(); i++) { + View c = getChildAt(i); + drawRectCorners(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(), + paint, lineLength, lineWidth); + } } } @@ -4613,13 +4707,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * Returns the basis of alignment during layout operations on this view group: - * either {@link #CLIP_BOUNDS} or {@link #OPTICAL_BOUNDS}. + * either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}. * * @return the layout mode to use during layout operations * * @see #setLayoutMode(int) - * - * @hide */ public int getLayoutMode() { return mLayoutMode; @@ -4627,15 +4719,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * Sets the basis of alignment during the layout of this view group. - * Valid values are either {@link #CLIP_BOUNDS} or {@link #OPTICAL_BOUNDS}. + * Valid values are either {@link #LAYOUT_MODE_CLIP_BOUNDS} or + * {@link #LAYOUT_MODE_OPTICAL_BOUNDS}. * <p> - * The default is {@link #CLIP_BOUNDS}. + * The default is {@link #LAYOUT_MODE_CLIP_BOUNDS}. * * @param layoutMode the layout mode to use during layout operations * * @see #getLayoutMode() - * - * @hide */ public void setLayoutMode(int layoutMode) { if (mLayoutMode != layoutMode) { @@ -5670,7 +5761,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * * @hide */ - public void onDebugDraw(View view, Canvas canvas) { + public void onDebugDraw(View view, Canvas canvas, Paint paint) { } /** @@ -6023,12 +6114,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @hide */ @Override - public void onDebugDraw(View view, Canvas canvas) { - drawRect(canvas, - view.getLeft() - leftMargin, - view.getTop() - topMargin, - view.getRight() + rightMargin, - view.getBottom() + bottomMargin, Color.MAGENTA); + public void onDebugDraw(View view, Canvas canvas, Paint paint) { + Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE; + + fillDifference(canvas, + view.getLeft() + oi.left, + view.getTop() + oi.top, + view.getRight() - oi.right, + view.getBottom() - oi.bottom, + leftMargin, + topMargin, + rightMargin, + bottomMargin, + paint); } } @@ -6362,14 +6460,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return sDebugPaint; } - private static float[] getDebugLines(int x1, int y1, int x2, int y2) { + private void drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) { if (sDebugLines== null) { sDebugLines = new float[16]; } - x2--; - y2--; - sDebugLines[0] = x1; sDebugLines[1] = y1; sDebugLines[2] = x2; @@ -6378,18 +6473,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager sDebugLines[4] = x2; sDebugLines[5] = y1; sDebugLines[6] = x2; - sDebugLines[7] = y2 + 1; + sDebugLines[7] = y2; - sDebugLines[8] = x2 + 1; + sDebugLines[8] = x2; sDebugLines[9] = y2; sDebugLines[10] = x1; sDebugLines[11] = y2; - sDebugLines[12] = x1; - sDebugLines[13] = y2; + sDebugLines[12] = x1; + sDebugLines[13] = y2; sDebugLines[14] = x1; sDebugLines[15] = y1; - return sDebugLines; + canvas.drawLines(sDebugLines, paint); } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 62bf691..51edc08 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -319,6 +319,11 @@ public final class ViewRootImpl implements ViewParent, private final int mDensity; private final int mNoncompatDensity; + private boolean mInLayout = false; + ArrayList<View> mLayoutRequesters = new ArrayList<View>(); + boolean mHandlingLayoutInLayoutRequest = false; + + /** * Consistency verifier for debugging purposes. */ @@ -1873,9 +1878,49 @@ public final class ViewRootImpl implements ViewParent, } } + /** + * Called by {@link android.view.View#isInLayout()} to determine whether the view hierarchy + * is currently undergoing a layout pass. + * + * @return whether the view hierarchy is currently undergoing a layout pass + */ + boolean isInLayout() { + return mInLayout; + } + + /** + * Called by {@link android.view.View#requestLayout()} if the view hiearchy is currently + * undergoing a layout pass. requestLayout() should not be called during layout, but if it + * is called anyway, we handle the situation here rather than leave the hierarchy in an + * indeterminate state. The solution is to queue up all requests during layout, apply those + * requests as soon as layout is complete, and then run layout once more immediately. If + * more requestLayout() calls are received during that second layout pass, we post those + * requests to the next frame, to avoid possible infinite loops. + * + * @param view the view that requested the layout. + */ + void requestLayoutDuringLayout(final View view) { + if (!mHandlingLayoutInLayoutRequest) { + if (!mLayoutRequesters.contains(view)) { + Log.w("View", "requestLayout() called by " + view + " during layout pass"); + mLayoutRequesters.add(view); + } + } else { + Log.w("View", "requestLayout() called by " + view + " during second layout pass: " + + "posting to next frame"); + view.post(new Runnable() { + @Override + public void run() { + view.requestLayout(); + } + }); + } + } + private void performLayout() { mLayoutRequested = false; mScrollMayChange = true; + mInLayout = true; final View host = mView; if (DEBUG_ORIENTATION || DEBUG_LAYOUT) { @@ -1886,9 +1931,22 @@ public final class ViewRootImpl implements ViewParent, Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout"); try { host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); + int numViewsRequestingLayout = mLayoutRequesters.size(); + if (numViewsRequestingLayout > 0) { + // requestLayout() was called during layout: unusual, but try to handle correctly + mHandlingLayoutInLayoutRequest = true; + for (int i = 0; i < numViewsRequestingLayout; ++i) { + mLayoutRequesters.get(i).requestLayout(); + } + // Now run layout one more time + host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); + mHandlingLayoutInLayoutRequest = false; + mLayoutRequesters.clear(); + } } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } + mInLayout = false; } public void requestTransparentRegion(View child) { diff --git a/core/java/android/webkit/EventLogTags.logtags b/core/java/android/webkit/EventLogTags.logtags index 082a437..b0b5493 100644 --- a/core/java/android/webkit/EventLogTags.logtags +++ b/core/java/android/webkit/EventLogTags.logtags @@ -8,4 +8,3 @@ option java_package android.webkit; # 70103- used by the browser app itself 70150 browser_snap_center -70151 browser_text_size_change (oldSize|1|5), (newSize|1|5) diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index aa68904..7f6fa1a 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -89,6 +89,14 @@ public abstract class WebSettings { ZoomDensity(int size) { value = size; } + + /** + * @hide Only for use by WebViewProvider implementations + */ + public int getValue() { + return value; + } + int value; } @@ -936,6 +944,9 @@ public abstract class WebSettings { * access to content from other file scheme URLs. See * {@link #setAllowFileAccessFromFileURLs}. To enable the most restrictive, * and therefore secure policy, this setting should be disabled. + * Note that this setting affects only JavaScript access to file scheme + * resources. Other access to such resources, for example, from image HTML + * elements, is unaffected. * <p> * The default value is true for API level * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below, @@ -953,6 +964,9 @@ public abstract class WebSettings { * enable the most restrictive, and therefore secure policy, this setting * should be disabled. Note that the value of this setting is ignored if * the value of {@link #getAllowUniversalAccessFromFileURLs} is true. + * Note too, that this setting affects only JavaScript access to file scheme + * resources. Other access to such resources, for example, from image HTML + * elements, is unaffected. * <p> * The default value is true for API level * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below, @@ -1121,9 +1135,22 @@ public abstract class WebSettings { } /** - * Sets whether Geolocation is enabled. The default is true. See also - * {@link #setGeolocationDatabasePath} for how to correctly set up - * Geolocation. + * Sets whether Geolocation is enabled. The default is true. + * <p> + * Please note that in order for the Geolocation API to be usable + * by a page in the WebView, the following requirements must be met: + * <ul> + * <li>an application must have permission to access the device location, + * see {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}; + * <li>an application must provide an implementation of the + * {@link WebChromeClient#onGeolocationPermissionsShowPrompt} callback + * to receive notifications that a page is requesting access to location + * via the JavaScript Geolocation API. + * </ul> + * <p> + * As an option, it is possible to store previous locations and web origin + * permissions in a database. See {@link #setGeolocationDatabasePath}. * * @param flag whether Geolocation should be enabled */ @@ -1295,7 +1322,7 @@ public abstract class WebSettings { * and content is re-validated as needed. When navigating back, content is * not revalidated, instead the content is just retrieved from the cache. * This method allows the client to override this behavior by specifying - * one of {@link #LOAD_DEFAULT}, {@link #LOAD_NORMAL}, + * one of {@link #LOAD_DEFAULT}, * {@link #LOAD_CACHE_ELSE_NETWORK}, {@link #LOAD_NO_CACHE} or * {@link #LOAD_CACHE_ONLY}. The default value is {@link #LOAD_DEFAULT}. * diff --git a/core/java/android/webkit/WebSettingsClassic.java b/core/java/android/webkit/WebSettingsClassic.java index 1bbe7bb..e3d095f 100644 --- a/core/java/android/webkit/WebSettingsClassic.java +++ b/core/java/android/webkit/WebSettingsClassic.java @@ -647,10 +647,6 @@ public class WebSettingsClassic extends WebSettings { @Override public synchronized void setTextZoom(int textZoom) { if (mTextSize != textZoom) { - if (WebViewClassic.mLogEvent) { - EventLog.writeEvent(EventLogTags.BROWSER_TEXT_SIZE_CHANGE, - mTextSize, textZoom); - } mTextSize = textZoom; postSync(); } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 6df7820..6aadaf9 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -2137,4 +2137,10 @@ public class WebView extends AbsoluteLayout super.setLayerType(layerType, paint); mProvider.getViewDelegate().setLayerType(layerType, paint); } + + @Override + protected void dispatchDraw(Canvas canvas) { + mProvider.getViewDelegate().preDispatchDraw(canvas); + super.dispatchDraw(canvas); + } } diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java index 0f8966e..ff3087e 100644 --- a/core/java/android/webkit/WebViewClassic.java +++ b/core/java/android/webkit/WebViewClassic.java @@ -8557,6 +8557,11 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc updateHwAccelerated(); } + @Override + public void preDispatchDraw(Canvas canvas) { + // no-op for WebViewClassic. + } + private void updateHwAccelerated() { if (mNativeClass == 0) { return; diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java index c9f9fbd..1020634 100644 --- a/core/java/android/webkit/WebViewProvider.java +++ b/core/java/android/webkit/WebViewProvider.java @@ -341,6 +341,8 @@ public interface WebViewProvider { public void setBackgroundColor(int color); public void setLayerType(int layerType, Paint paint); + + public void preDispatchDraw(Canvas canvas); } interface ScrollDelegate { diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java index f1804f8..41ab5f2 100644 --- a/core/java/android/widget/CheckBox.java +++ b/core/java/android/widget/CheckBox.java @@ -20,6 +20,7 @@ import android.content.Context; import android.util.AttributeSet; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; +import android.util.ValueModel; /** @@ -55,7 +56,9 @@ import android.view.accessibility.AccessibilityNodeInfo; * {@link android.R.styleable#View View Attributes} * </p> */ -public class CheckBox extends CompoundButton { +public class CheckBox extends CompoundButton implements ValueEditor<Boolean> { + private ValueModel<Boolean> mValueModel = ValueModel.EMPTY; + public CheckBox(Context context) { this(context, null); } @@ -79,4 +82,22 @@ public class CheckBox extends CompoundButton { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(CheckBox.class.getName()); } + + @Override + public ValueModel<Boolean> getValueModel() { + return mValueModel; + } + + @Override + public void setValueModel(ValueModel<Boolean> valueModel) { + mValueModel = valueModel; + setChecked(mValueModel.get()); + } + + @Override + public boolean performClick() { + boolean handled = super.performClick(); + mValueModel.set(isChecked()); + return handled; + } } diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java index 57e51c2..ec81214 100644 --- a/core/java/android/widget/EditText.java +++ b/core/java/android/widget/EditText.java @@ -17,6 +17,7 @@ package android.widget; import android.content.Context; +import android.graphics.Rect; import android.text.Editable; import android.text.Selection; import android.text.Spannable; @@ -24,6 +25,7 @@ import android.text.TextUtils; import android.text.method.ArrowKeyMovementMethod; import android.text.method.MovementMethod; import android.util.AttributeSet; +import android.util.ValueModel; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -47,7 +49,9 @@ import android.view.accessibility.AccessibilityNodeInfo; * {@link android.R.styleable#TextView TextView Attributes}, * {@link android.R.styleable#View View Attributes} */ -public class EditText extends TextView { +public class EditText extends TextView implements ValueEditor<CharSequence> { + private ValueModel<CharSequence> mValueModel = ValueModel.EMPTY; + public EditText(Context context) { this(context, null); } @@ -128,4 +132,21 @@ public class EditText extends TextView { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(EditText.class.getName()); } + + @Override + public ValueModel<CharSequence> getValueModel() { + return mValueModel; + } + + @Override + public void setValueModel(ValueModel<CharSequence> valueModel) { + mValueModel = valueModel; + setText(mValueModel.get()); + } + + @Override + void sendAfterTextChanged(Editable text) { + super.sendAfterTextChanged(text); + mValueModel.set(text); + } } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 19b825c..8180890 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -2612,15 +2612,10 @@ public class Editor { suggestionStart, suggestionEnd).toString(); mTextView.replaceText_internal(spanStart, spanEnd, suggestion); - // Notify source IME of the suggestion pick. Do this before swaping texts. - if (!TextUtils.isEmpty( - suggestionInfo.suggestionSpan.getNotificationTargetClassName())) { - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - imm.notifySuggestionPicked(suggestionInfo.suggestionSpan, originalText, - suggestionInfo.suggestionIndex); - } - } + // Notify source IME of the suggestion pick. Do this before + // swaping texts. + suggestionInfo.suggestionSpan.notifySelection( + mTextView.getContext(), originalText, suggestionInfo.suggestionIndex); // Swap text content between actual text and Suggestion span String[] suggestions = suggestionInfo.suggestionSpan.getSuggestions(); diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java index 772d748..85ed8db 100644 --- a/core/java/android/widget/GridLayout.java +++ b/core/java/android/widget/GridLayout.java @@ -605,7 +605,7 @@ public class GridLayout extends ViewGroup { } private int getDefaultMargin(View c, boolean isAtEdge, boolean horizontal, boolean leading) { - return isAtEdge ? DEFAULT_CONTAINER_MARGIN : getDefaultMargin(c, horizontal, leading); + return /*isAtEdge ? DEFAULT_CONTAINER_MARGIN :*/ getDefaultMargin(c, horizontal, leading); } private int getDefaultMargin(View c, LayoutParams p, boolean horizontal, boolean leading) { @@ -824,13 +824,11 @@ public class GridLayout extends ViewGroup { // Draw grid private void drawLine(Canvas graphics, int x1, int y1, int x2, int y2, Paint paint) { - int dx = getPaddingLeft(); - int dy = getPaddingTop(); if (isLayoutRtl()) { int width = getWidth(); - graphics.drawLine(width - dx - x1, dy + y1, width - dx - x2, dy + y2, paint); + graphics.drawLine(width - x1, y1, width - x2, y2, paint); } else { - graphics.drawLine(dx + x1, dy + y1, dx + x2, dy + y2, paint); + graphics.drawLine(x1, y1, x2, y2, paint); } } @@ -838,18 +836,17 @@ public class GridLayout extends ViewGroup { * @hide */ @Override - protected void onDebugDrawMargins(Canvas canvas) { + protected void onDebugDrawMargins(Canvas canvas, Paint paint) { // Apply defaults, so as to remove UNDEFINED values LayoutParams lp = new LayoutParams(); for (int i = 0; i < getChildCount(); i++) { View c = getChildAt(i); - Insets insets = getLayoutMode() == OPTICAL_BOUNDS ? c.getOpticalInsets() : Insets.NONE; lp.setMargins( - getMargin1(c, true, true) - insets.left, - getMargin1(c, false, true) - insets.top, - getMargin1(c, true, false) - insets.right, - getMargin1(c, false, false) - insets.bottom); - lp.onDebugDraw(c, canvas); + getMargin1(c, true, true), + getMargin1(c, false, true), + getMargin1(c, true, false), + getMargin1(c, false, false)); + lp.onDebugDraw(c, canvas, paint); } } @@ -858,26 +855,30 @@ public class GridLayout extends ViewGroup { */ @Override protected void onDebugDraw(Canvas canvas) { - int height = getHeight() - getPaddingTop() - getPaddingBottom(); - int width = getWidth() - getPaddingLeft() - getPaddingRight(); - Paint paint = new Paint(); paint.setStyle(Paint.Style.STROKE); paint.setColor(Color.argb(50, 255, 255, 255)); + Insets insets = getOpticalInsets(); + + int top = getPaddingTop() + insets.top; + int left = getPaddingLeft() + insets.left; + int right = getWidth() - getPaddingRight() - insets.right; + int bottom = getHeight() - getPaddingBottom() - insets.bottom; + int[] xs = horizontalAxis.locations; if (xs != null) { for (int i = 0, length = xs.length; i < length; i++) { - int x = xs[i]; - drawLine(canvas, x, 0, x, height - 1, paint); + int x = left + xs[i]; + drawLine(canvas, x, top, x, bottom, paint); } } int[] ys = verticalAxis.locations; if (ys != null) { for (int i = 0, length = ys.length; i < length; i++) { - int y = ys[i]; - drawLine(canvas, 0, y, width - 1, y, paint); + int y = top + ys[i]; + drawLine(canvas, left, y, right, y, paint); } } @@ -1013,12 +1014,7 @@ public class GridLayout extends ViewGroup { } private int getMeasurement(View c, boolean horizontal) { - int result = horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight(); - if (getLayoutMode() == OPTICAL_BOUNDS) { - Insets insets = c.getOpticalInsets(); - return result - (horizontal ? insets.left + insets.right : insets.top + insets.bottom); - } - return result; + return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight(); } final int getMeasurementIncludingMargin(View c, boolean horizontal) { @@ -1124,14 +1120,6 @@ public class GridLayout extends ViewGroup { targetWidth - width - paddingRight - rightMargin - dx; int cy = paddingTop + y1 + gravityOffsetY + alignmentOffsetY + topMargin; - boolean useLayoutBounds = getLayoutMode() == OPTICAL_BOUNDS; - if (useLayoutBounds) { - Insets insets = c.getOpticalInsets(); - cx -= insets.left; - cy -= insets.top; - width += (insets.left + insets.right); - height += (insets.top + insets.bottom); - } if (width != c.getMeasuredWidth() || height != c.getMeasuredHeight()) { c.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY)); } @@ -2418,6 +2406,8 @@ public class GridLayout extends ViewGroup { * <li> {@code spec.span = [start, start + size]} </li> * <li> {@code spec.alignment = alignment} </li> * </ul> + * <p> + * To leave the start index undefined, use the value {@link #UNDEFINED}. * * @param start the start * @param size the size @@ -2433,9 +2423,13 @@ public class GridLayout extends ViewGroup { * <li> {@code spec.span = [start, start + 1]} </li> * <li> {@code spec.alignment = alignment} </li> * </ul> + * <p> + * To leave the start index undefined, use the value {@link #UNDEFINED}. * * @param start the start index * @param alignment the alignment + * + * @see #spec(int, int, Alignment) */ public static Spec spec(int start, Alignment alignment) { return spec(start, 1, alignment); @@ -2446,9 +2440,13 @@ public class GridLayout extends ViewGroup { * <ul> * <li> {@code spec.span = [start, start + size]} </li> * </ul> + * <p> + * To leave the start index undefined, use the value {@link #UNDEFINED}. * * @param start the start * @param size the size + * + * @see #spec(int, Alignment) */ public static Spec spec(int start, int size) { return spec(start, size, UNDEFINED_ALIGNMENT); @@ -2459,8 +2457,12 @@ public class GridLayout extends ViewGroup { * <ul> * <li> {@code spec.span = [start, start + 1]} </li> * </ul> + * <p> + * To leave the start index undefined, use the value {@link #UNDEFINED}. * * @param start the start index + * + * @see #spec(int, int) */ public static Spec spec(int start) { return spec(start, 1); @@ -2654,14 +2656,7 @@ public class GridLayout extends ViewGroup { @Override public int getAlignmentValue(View view, int viewSize, int mode) { int baseline = view.getBaseline(); - if (baseline == -1) { - return UNDEFINED; - } else { - if (mode == OPTICAL_BOUNDS) { - return baseline - view.getOpticalInsets().top; - } - return baseline; - } + return baseline == -1 ? UNDEFINED : baseline; } @Override diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 87396fb..1d465ce 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -789,6 +789,12 @@ public class ImageView extends View { if (resizeWidth) { int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright; + + // Allow the width to outgrow its original estimate if height is fixed. + if (!resizeHeight) { + widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec); + } + if (newWidth <= widthSize) { widthSize = newWidth; done = true; @@ -799,6 +805,13 @@ public class ImageView extends View { if (!done && resizeHeight) { int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom; + + // Allow the height to outgrow its original estimate if width is fixed. + if (!resizeWidth) { + heightSize = resolveAdjustedSize(newHeight, mMaxHeight, + heightMeasureSpec); + } + if (newHeight <= heightSize) { heightSize = newHeight; } diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 83c15bb..abf2756 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -1437,9 +1437,9 @@ public class LinearLayout extends ViewGroup { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (mOrientation == VERTICAL) { - layoutVertical(); + layoutVertical(l, t, r, b); } else { - layoutHorizontal(); + layoutHorizontal(l, t, r, b); } } @@ -1450,15 +1450,19 @@ public class LinearLayout extends ViewGroup { * @see #getOrientation() * @see #setOrientation(int) * @see #onLayout(boolean, int, int, int, int) + * @param left + * @param top + * @param right + * @param bottom */ - void layoutVertical() { + void layoutVertical(int left, int top, int right, int bottom) { final int paddingLeft = mPaddingLeft; int childTop; int childLeft; // Where right end of child should go - final int width = mRight - mLeft; + final int width = right - left; int childRight = width - mPaddingRight; // Space available for child @@ -1472,12 +1476,12 @@ public class LinearLayout extends ViewGroup { switch (majorGravity) { case Gravity.BOTTOM: // mTotalLength contains the padding already - childTop = mPaddingTop + mBottom - mTop - mTotalLength; + childTop = mPaddingTop + bottom - top - mTotalLength; break; // mTotalLength contains the padding already case Gravity.CENTER_VERTICAL: - childTop = mPaddingTop + (mBottom - mTop - mTotalLength) / 2; + childTop = mPaddingTop + (bottom - top - mTotalLength) / 2; break; case Gravity.TOP: @@ -1540,8 +1544,12 @@ public class LinearLayout extends ViewGroup { * @see #getOrientation() * @see #setOrientation(int) * @see #onLayout(boolean, int, int, int, int) + * @param left + * @param top + * @param right + * @param bottom */ - void layoutHorizontal() { + void layoutHorizontal(int left, int top, int right, int bottom) { final boolean isLayoutRtl = isLayoutRtl(); final int paddingTop = mPaddingTop; @@ -1549,7 +1557,7 @@ public class LinearLayout extends ViewGroup { int childLeft; // Where bottom of child should go - final int height = mBottom - mTop; + final int height = bottom - top; int childBottom = height - mPaddingBottom; // Space available for child @@ -1569,12 +1577,12 @@ public class LinearLayout extends ViewGroup { switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) { case Gravity.RIGHT: // mTotalLength contains the padding already - childLeft = mPaddingLeft + mRight - mLeft - mTotalLength; + childLeft = mPaddingLeft + right - left - mTotalLength; break; case Gravity.CENTER_HORIZONTAL: // mTotalLength contains the padding already - childLeft = mPaddingLeft + (mRight - mLeft - mTotalLength) / 2; + childLeft = mPaddingLeft + (right - left - mTotalLength) / 2; break; case Gravity.LEFT: diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index ace26f3..de5b369 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -369,10 +369,10 @@ public class RelativeLayout extends ViewGroup { int width = 0; int height = 0; - int widthMode = MeasureSpec.getMode(widthMeasureSpec); - int heightMode = MeasureSpec.getMode(heightMeasureSpec); - int widthSize = MeasureSpec.getSize(widthMeasureSpec); - int heightSize = MeasureSpec.getSize(heightMeasureSpec); + final int widthMode = MeasureSpec.getMode(widthMeasureSpec); + final int heightMode = MeasureSpec.getMode(heightMeasureSpec); + final int widthSize = MeasureSpec.getSize(widthMeasureSpec); + final int heightSize = MeasureSpec.getSize(heightMeasureSpec); // Record our dimensions if they are known; if (widthMode != MeasureSpec.UNSPECIFIED) { @@ -638,7 +638,12 @@ public class RelativeLayout extends ViewGroup { mPaddingLeft, mPaddingRight, myWidth); int childHeightMeasureSpec; - if (params.width == LayoutParams.MATCH_PARENT) { + if (myHeight < 0) { + // Negative values in a mySize/myWidth/myWidth value in RelativeLayout measurement + // is code for, "we got an unspecified mode in the RelativeLayout's measurespec." + // Carry it forward. + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + } else if (params.width == LayoutParams.MATCH_PARENT) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST); @@ -665,6 +670,13 @@ public class RelativeLayout extends ViewGroup { private int getChildMeasureSpec(int childStart, int childEnd, int childSize, int startMargin, int endMargin, int startPadding, int endPadding, int mySize) { + if (mySize < 0) { + // Negative values in a mySize/myWidth/myWidth value in RelativeLayout measurement + // is code for, "we got an unspecified mode in the RelativeLayout's measurespec." + // Carry it forward. + return MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + } + int childSpecMode = 0; int childSpecSize = 0; diff --git a/core/java/android/widget/SeekBar.java b/core/java/android/widget/SeekBar.java index 2737f94..a6486a8 100644 --- a/core/java/android/widget/SeekBar.java +++ b/core/java/android/widget/SeekBar.java @@ -18,6 +18,7 @@ package android.widget; import android.content.Context; import android.util.AttributeSet; +import android.util.ValueModel; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -33,7 +34,7 @@ import android.view.accessibility.AccessibilityNodeInfo; * * @attr ref android.R.styleable#SeekBar_thumb */ -public class SeekBar extends AbsSeekBar { +public class SeekBar extends AbsSeekBar implements ValueEditor<Integer> { /** * A callback that notifies clients when the progress level has been @@ -69,8 +70,9 @@ public class SeekBar extends AbsSeekBar { void onStopTrackingTouch(SeekBar seekBar); } + private ValueModel<Integer> mValueModel = ValueModel.EMPTY; private OnSeekBarChangeListener mOnSeekBarChangeListener; - + public SeekBar(Context context) { this(context, null); } @@ -89,9 +91,23 @@ public class SeekBar extends AbsSeekBar { if (mOnSeekBarChangeListener != null) { mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser); + if (fromUser) { + mValueModel.set(getProgress()); + } } } + @Override + public ValueModel<Integer> getValueModel() { + return mValueModel; + } + + @Override + public void setValueModel(ValueModel<Integer> valueModel) { + mValueModel = valueModel; + setProgress(mValueModel.get()); + } + /** * Sets a listener to receive notifications of changes to the SeekBar's progress level. Also * provides notifications of when the user starts and stops a touch gesture within the SeekBar. diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java index 399b4fa..f4b2ce0 100644 --- a/core/java/android/widget/TableLayout.java +++ b/core/java/android/widget/TableLayout.java @@ -445,7 +445,7 @@ public class TableLayout extends LinearLayout { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // enforce vertical layout - layoutVertical(); + layoutVertical(l, t, r, b); } /** diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java index 68ffd73..a8cc406 100644 --- a/core/java/android/widget/TableRow.java +++ b/core/java/android/widget/TableRow.java @@ -120,7 +120,7 @@ public class TableRow extends LinearLayout { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // enforce horizontal layout - layoutHorizontal(); + layoutHorizontal(l, t, r, b); } /** diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 8e5ff40..8d00444 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -26,6 +26,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.Canvas; +import android.graphics.Insets; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; @@ -4220,6 +4221,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener ///////////////////////////////////////////////////////////////////////// + private int getBoxHeight(Layout l) { + Insets opticalInsets = isLayoutModeOptical(mParent) ? getOpticalInsets() : Insets.NONE; + int padding = (l == mHintLayout) ? + getCompoundPaddingTop() + getCompoundPaddingBottom() : + getExtendedPaddingTop() + getExtendedPaddingBottom(); + return getMeasuredHeight() - padding + opticalInsets.top + opticalInsets.bottom; + } + int getVerticalOffset(boolean forceNormal) { int voffset = 0; final int gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; @@ -4230,15 +4239,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (gravity != Gravity.TOP) { - int boxht; - - if (l == mHintLayout) { - boxht = getMeasuredHeight() - getCompoundPaddingTop() - - getCompoundPaddingBottom(); - } else { - boxht = getMeasuredHeight() - getExtendedPaddingTop() - - getExtendedPaddingBottom(); - } + int boxht = getBoxHeight(l); int textht = l.getHeight(); if (textht < boxht) { @@ -4261,15 +4262,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (gravity != Gravity.BOTTOM) { - int boxht; - - if (l == mHintLayout) { - boxht = getMeasuredHeight() - getCompoundPaddingTop() - - getCompoundPaddingBottom(); - } else { - boxht = getMeasuredHeight() - getExtendedPaddingTop() - - getExtendedPaddingBottom(); - } + int boxht = getBoxHeight(l); int textht = l.getHeight(); if (textht < boxht) { @@ -5005,6 +4998,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener voffset = getVerticalOffset(true); } + if (isLayoutModeOptical(mParent)) { + voffset -= getOpticalInsets().top; + } + return getExtendedPaddingTop() + voffset + mLayout.getLineBaseline(0); } diff --git a/core/java/android/widget/ValueEditor.java b/core/java/android/widget/ValueEditor.java new file mode 100755 index 0000000..2b91abf --- /dev/null +++ b/core/java/android/widget/ValueEditor.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012 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.util.ValueModel; + +/** + * An interface for editors of simple values. Classes implementing this interface are normally + * UI controls (subclasses of {@link android.view.View View}) that can provide a suitable + * user interface to display and edit values of the specified type. This interface is + * intended to describe editors for simple types, like {@code boolean}, {@code int} or + * {@code String}, where the values themselves are immutable. + * <p> + * For example, {@link android.widget.CheckBox CheckBox} implements + * this interface for the Boolean type as it is capable of providing an appropriate + * mechanism for displaying and changing the value of a Boolean property. + * + * @param <T> the value type that this editor supports + */ +public interface ValueEditor<T> { + /** + * Return the last value model that was set. If no value model has been set, the editor + * should return the value {@link android.util.ValueModel#EMPTY}. + * + * @return the value model + */ + public ValueModel<T> getValueModel(); + + /** + * Sets the value model for this editor. When the value model is set, the editor should + * retrieve the value from the value model, using {@link android.util.ValueModel#get()}, + * and set its internal state accordingly. Likewise, when the editor's internal state changes + * it should update the value model by calling {@link android.util.ValueModel#set(T)} + * with the appropriate value. + * + * @param valueModel the new value model for this editor. + */ + public void setValueModel(ValueModel<T> valueModel); +} |