diff options
Diffstat (limited to 'core/java/android')
24 files changed, 408 insertions, 168 deletions
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index a0f40f6..b3aa6be 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -99,8 +99,8 @@ import android.os.Vibrator; import android.os.storage.StorageManager; import android.print.IPrintManager; import android.print.PrintManager; -import android.service.fingerprint.FingerprintManager; -import android.service.fingerprint.IFingerprintService; +import android.hardware.fingerprint.FingerprintManager; +import android.hardware.fingerprint.IFingerprintService; import android.service.persistentdata.IPersistentDataBlockService; import android.service.persistentdata.PersistentDataBlockManager; import android.telecom.TelecomManager; diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index be26eac..edb768d 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; @@ -206,6 +207,23 @@ public final class BluetoothAdapter { "android.bluetooth.adapter.action.REQUEST_ENABLE"; /** + * Activity Action: Show a system activity that allows user to enable BLE scans even when + * Bluetooth is turned off.<p> + * + * Notification of result of this activity is posted using + * {@link android.app.Activity#onActivityResult}. The <code>resultCode</code> will be + * {@link android.app.Activity#RESULT_OK} if BLE scan always available setting is turned on or + * {@link android.app.Activity#RESULT_CANCELED} if the user has rejected the request or an + * error occurred. + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = + "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE"; + + /** * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter * has changed. * <p>Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link @@ -916,6 +934,22 @@ public final class BluetoothAdapter { } /** + * Returns {@code true} if BLE scan is always available, {@code false} otherwise. <p> + * + * If this returns {@code true}, application can issue {@link BluetoothLeScanner#startScan} and + * fetch scan results even when Bluetooth is turned off.<p> + * + * To change this setting, use {@link #ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE}. + * + * @hide + */ + @SystemApi + public boolean isBleScanAlwaysAvailable() { + // TODO: implement after Settings UI change. + return false; + } + + /** * Returns whether peripheral mode is supported. * * @hide diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 7a99a79..514802e9 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1041,6 +1041,18 @@ public class Intent implements Parcelable, Cloneable { */ public static final String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED"; /** + * Activity action: Activate the current SIM card. If SIM cards do not require activation, + * sending this intent is a no-op. + * <p>Input: No data should be specified. get*Extra may have an optional + * {@link #EXTRA_SIM_ACTIVATION_RESPONSE} field containing a PendingIntent through which to + * send the activation result. + * <p>Output: nothing. + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_SIM_ACTIVATION_REQUEST = + "android.intent.action.SIM_ACTIVATION_REQUEST"; + /** * Activity Action: Send a message to someone specified by the data. * <p>Input: {@link #getData} is URI describing the target. * <p>Output: nothing. @@ -3620,6 +3632,15 @@ public class Intent implements Parcelable, Cloneable { /** {@hide} */ public static final String EXTRA_REASON = "android.intent.extra.REASON"; + /** + * Optional {@link android.app.PendingIntent} extra used to deliver the result of the SIM + * activation request. + * TODO: Add information about the structure and response data used with the pending intent. + * @hide + */ + public static final String EXTRA_SIM_ACTIVATION_RESPONSE = + "android.intent.extra.SIM_ACTIVATION_RESPONSE"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Intent flags (see mFlags variable). diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java index 7d8dff3..fdafb04 100644 --- a/core/java/android/content/res/ColorStateList.java +++ b/core/java/android/content/res/ColorStateList.java @@ -71,10 +71,15 @@ import java.util.Arrays; */ public class ColorStateList implements Parcelable { private static final String TAG = "ColorStateList"; + private static final int DEFAULT_COLOR = Color.RED; private static final int[][] EMPTY = new int[][] { new int[0] }; - private static final SparseArray<WeakReference<ColorStateList>> sCache = - new SparseArray<WeakReference<ColorStateList>>(); + + /** Thread-safe cache of single-color ColorStateLists. */ + private static final SparseArray<WeakReference<ColorStateList>> sCache = new SparseArray<>(); + + /** Lazily-created factory for this color state list. */ + private ColorStateListFactory mFactory; private int[][] mThemeAttrs; private int mChangingConfigurations; @@ -125,7 +130,7 @@ public class ColorStateList implements Parcelable { } final ColorStateList csl = new ColorStateList(EMPTY, new int[] { color }); - sCache.put(color, new WeakReference<ColorStateList>(csl)); + sCache.put(color, new WeakReference<>(csl)); return csl; } } @@ -141,11 +146,13 @@ public class ColorStateList implements Parcelable { */ private ColorStateList(ColorStateList orig) { if (orig != null) { + mChangingConfigurations = orig.mChangingConfigurations; mStateSpecs = orig.mStateSpecs; mDefaultColor = orig.mDefaultColor; mIsOpaque = orig.mIsOpaque; - // Deep copy, this may change due to theming. + // Deep copy, these may change due to applyTheme(). + mThemeAttrs = orig.mThemeAttrs.clone(); mColors = orig.mColors.clone(); } } @@ -329,6 +336,7 @@ public class ColorStateList implements Parcelable { * attributes. * * @return whether a theme can be applied to this color state list + * @hide only for resource preloading */ public boolean canApplyTheme() { return mThemeAttrs != null; @@ -336,10 +344,15 @@ public class ColorStateList implements Parcelable { /** * Applies a theme to this color state list. + * <p> + * <strong>Note:</strong> Applying a theme may affect the changing + * configuration parameters of this color state list. After calling this + * method, any dependent configurations must be updated by obtaining the + * new configuration mask from {@link #getChangingConfigurations()}. * * @param t the theme to apply */ - public void applyTheme(Theme t) { + private void applyTheme(Theme t) { if (mThemeAttrs == null) { return; } @@ -376,6 +389,38 @@ public class ColorStateList implements Parcelable { onColorsChanged(); } + /** + * Returns an appropriately themed color state list. + * + * @param t the theme to apply + * @return a copy of the color state list with the theme applied, or the + * color state list itself if there were no unresolved theme + * attributes + * @hide only for resource preloading + */ + public ColorStateList obtainForTheme(Theme t) { + if (t == null || !canApplyTheme()) { + return this; + } + + final ColorStateList clone = new ColorStateList(this); + clone.applyTheme(t); + return clone; + } + + /** + * Returns a mask of the configuration parameters for which this color + * state list may change, requiring that it be re-created. + * + * @return a mask of the changing configuration parameters, as defined by + * {@link android.content.pm.ActivityInfo} + * + * @see android.content.pm.ActivityInfo + */ + public int getChangingConfigurations() { + return mChangingConfigurations; + } + private int modulateColorAlpha(int baseColor, float alphaMod) { if (alphaMod == 1.0f) { return baseColor; @@ -383,8 +428,7 @@ public class ColorStateList implements Parcelable { final int baseAlpha = Color.alpha(baseColor); final int alpha = MathUtils.constrain((int) (baseAlpha * alphaMod + 0.5f), 0, 255); - final int color = (baseColor & 0xFFFFFF) | (alpha << 24); - return color; + return (baseColor & 0xFFFFFF) | (alpha << 24); } /** @@ -534,14 +578,18 @@ public class ColorStateList implements Parcelable { } /** - * @return A factory that can create new instances of this ColorStateList. + * @return a factory that can create new instances of this ColorStateList + * @hide only for resource preloading */ - ColorStateListFactory getFactory() { - return new ColorStateListFactory(this); + public ConstantState<ColorStateList> getConstantState() { + if (mFactory != null) { + mFactory = new ColorStateListFactory(this); + } + return mFactory; } - static class ColorStateListFactory extends ConstantState<ColorStateList> { - final ColorStateList mSrc; + private static class ColorStateListFactory extends ConstantState<ColorStateList> { + private final ColorStateList mSrc; public ColorStateListFactory(ColorStateList src) { mSrc = src; @@ -559,13 +607,7 @@ public class ColorStateList implements Parcelable { @Override public ColorStateList newInstance(Resources res, Theme theme) { - if (theme == null || !mSrc.canApplyTheme()) { - return mSrc; - } - - final ColorStateList clone = new ColorStateList(mSrc); - clone.applyTheme(theme); - return clone; + return mSrc.obtainForTheme(theme); } } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 44018ff..7ce3721 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -41,7 +41,6 @@ import android.annotation.RawRes; import android.annotation.StringRes; import android.annotation.XmlRes; import android.content.pm.ActivityInfo; -import android.content.res.ColorStateList.ColorStateListFactory; import android.graphics.Movie; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -112,8 +111,8 @@ public class Resources { private static final LongSparseArray<ConstantState>[] sPreloadedDrawables; private static final LongSparseArray<ConstantState> sPreloadedColorDrawables = new LongSparseArray<>(); - private static final LongSparseArray<ColorStateListFactory> sPreloadedColorStateLists - = new LongSparseArray<>(); + private static final LongSparseArray<android.content.res.ConstantState<ColorStateList>> + sPreloadedColorStateLists = new LongSparseArray<>(); // Pool of TypedArrays targeted to this Resources object. final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5); @@ -2667,7 +2666,8 @@ public class Resources { // Handle inline color definitions. if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && value.type <= TypedValue.TYPE_LAST_COLOR_INT) { - final ColorStateListFactory factory = sPreloadedColorStateLists.get(key); + final android.content.res.ConstantState<ColorStateList> factory = + sPreloadedColorStateLists.get(key); if (factory != null) { return factory.newInstance(); } @@ -2677,7 +2677,7 @@ public class Resources { if (mPreloading) { if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId, "color")) { - sPreloadedColorStateLists.put(key, csl.getFactory()); + sPreloadedColorStateLists.put(key, csl.getConstantState()); } } @@ -2691,7 +2691,8 @@ public class Resources { return csl; } - final ColorStateListFactory factory = sPreloadedColorStateLists.get(key); + final android.content.res.ConstantState<ColorStateList> factory = + sPreloadedColorStateLists.get(key); if (factory != null) { csl = factory.newInstance(this, theme); } @@ -2704,10 +2705,10 @@ public class Resources { if (mPreloading) { if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId, "color")) { - sPreloadedColorStateLists.put(key, csl.getFactory()); + sPreloadedColorStateLists.put(key, csl.getConstantState()); } } else { - cache.put(key, theme, csl.getFactory()); + cache.put(key, theme, csl.getConstantState()); } } diff --git a/core/java/android/service/fingerprint/Fingerprint.aidl b/core/java/android/hardware/fingerprint/Fingerprint.aidl index c9fd989..4743354 100644 --- a/core/java/android/service/fingerprint/Fingerprint.aidl +++ b/core/java/android/hardware/fingerprint/Fingerprint.aidl @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.service.fingerprint; +package android.hardware.fingerprint; // @hide parcelable Fingerprint; diff --git a/core/java/android/service/fingerprint/Fingerprint.java b/core/java/android/hardware/fingerprint/Fingerprint.java index 37552eb..c307634 100644 --- a/core/java/android/service/fingerprint/Fingerprint.java +++ b/core/java/android/hardware/fingerprint/Fingerprint.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.service.fingerprint; +package android.hardware.fingerprint; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/service/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index bb90e40..e3572a2 100644 --- a/core/java/android/service/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.service.fingerprint; +package android.hardware.fingerprint; import android.app.ActivityManagerNative; import android.content.ContentResolver; @@ -28,7 +28,7 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; -import android.service.fingerprint.FingerprintManager.EnrollmentCallback; +import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback; import android.util.Log; import android.util.Slog; diff --git a/core/java/android/service/fingerprint/FingerprintUtils.java b/core/java/android/hardware/fingerprint/FingerprintUtils.java index 62acbb9..ae3d4a4 100644 --- a/core/java/android/service/fingerprint/FingerprintUtils.java +++ b/core/java/android/hardware/fingerprint/FingerprintUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.service.fingerprint; +package android.hardware.fingerprint; import android.content.ContentResolver; import android.provider.Settings; diff --git a/core/java/android/service/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index e5d3ad4..c5a45e2 100644 --- a/core/java/android/service/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.service.fingerprint; +package android.hardware.fingerprint; import android.os.Bundle; -import android.service.fingerprint.IFingerprintServiceReceiver; -import android.service.fingerprint.Fingerprint; +import android.hardware.fingerprint.IFingerprintServiceReceiver; +import android.hardware.fingerprint.Fingerprint; import java.util.List; /** diff --git a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl index f025064..e82395f 100644 --- a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.service.fingerprint; +package android.hardware.fingerprint; import android.os.Bundle; import android.os.UserHandle; diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index bf3d9aa..f305b2a 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -366,7 +366,6 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { public String toSafeString() { String scheme = getScheme(); String ssp = getSchemeSpecificPart(); - String authority = null; if (scheme != null) { if (scheme.equalsIgnoreCase("tel") || scheme.equalsIgnoreCase("sip") || scheme.equalsIgnoreCase("sms") || scheme.equalsIgnoreCase("smsto") @@ -385,9 +384,11 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { } } return builder.toString(); - } else if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")) { - ssp = null; - authority = "//" + getAuthority() + "/..."; + } else if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https") + || scheme.equalsIgnoreCase("ftp")) { + ssp = "//" + ((getHost() != null) ? getHost() : "") + + ((getPort() != -1) ? (":" + getPort()) : "") + + "/..."; } } // Not a sensitive scheme, but let's still be conservative about @@ -401,9 +402,6 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { if (ssp != null) { builder.append(ssp); } - if (authority != null) { - builder.append(authority); - } return builder.toString(); } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 8b3ecae..508fdee 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -4415,7 +4415,7 @@ public abstract class BatteryStats implements Parcelable { if (!checkin) { pw.println(header); } - String[] lineArgs = new String[4]; + String[] lineArgs = new String[5]; for (int i=0; i<count; i++) { long duration = steps.getDurationAt(i); int level = steps.getLevelAt(i); @@ -4430,7 +4430,7 @@ public abstract class BatteryStats implements Parcelable { case Display.STATE_ON: lineArgs[2] = "s+"; break; case Display.STATE_DOZE: lineArgs[2] = "sd"; break; case Display.STATE_DOZE_SUSPEND: lineArgs[2] = "sds"; break; - default: lineArgs[1] = "?"; break; + default: lineArgs[2] = "?"; break; } } else { lineArgs[2] = ""; @@ -4441,9 +4441,9 @@ public abstract class BatteryStats implements Parcelable { lineArgs[3] = ""; } if ((modMode&STEP_LEVEL_MODE_DEVICE_IDLE) == 0) { - lineArgs[3] = (initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0 ? "i+" : "i-"; + lineArgs[4] = (initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0 ? "i+" : "i-"; } else { - lineArgs[3] = ""; + lineArgs[4] = ""; } dumpLine(pw, 0 /* uid */, "i" /* category */, header, (Object[])lineArgs); } else { @@ -4459,7 +4459,7 @@ public abstract class BatteryStats implements Parcelable { case Display.STATE_ON: pw.print("screen-on"); break; case Display.STATE_DOZE: pw.print("screen-doze"); break; case Display.STATE_DOZE_SUSPEND: pw.print("screen-doze-suspend"); break; - default: lineArgs[1] = "screen-?"; break; + default: pw.print("screen-?"); break; } haveModes = true; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8e5d245..f79ef35 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6375,6 +6375,14 @@ public final class Settings { "wifi_scan_always_enabled"; /** + * Settings to allow BLE scans to be enabled even when Bluetooth is turned off for + * connectivity. + * @hide + */ + public static final String BLE_SCAN_ALWAYS_AVAILABLE = + "ble_scan_always_enabled"; + + /** * Used to save the Wifi_ON state prior to tethering. * This state will be checked to restore Wifi after * the user turns off tethering. diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index 7d2e1ef..239b386 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -79,7 +79,8 @@ public class DynamicLayout extends Layout boolean includepad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { this(base, display, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR, - spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth); + spacingmult, spacingadd, includepad, StaticLayout.BREAK_STRATEGY_SIMPLE, + ellipsize, ellipsizedWidth); } /** @@ -95,7 +96,7 @@ public class DynamicLayout extends Layout TextPaint paint, int width, Alignment align, TextDirectionHeuristic textDir, float spacingmult, float spacingadd, - boolean includepad, + boolean includepad, int breakStrategy, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { super((ellipsize == null) ? display @@ -120,6 +121,7 @@ public class DynamicLayout extends Layout mObjects = new PackedObjectVector<Directions>(1); mIncludePad = includepad; + mBreakStrategy = breakStrategy; /* * This is annoying, but we can't refer to the layout until @@ -279,10 +281,9 @@ public class DynamicLayout extends Layout sBuilder = null; } - // TODO: make sure reflowed is properly initialized if (reflowed == null) { reflowed = new StaticLayout(null); - b = StaticLayout.Builder.obtain(); + b = StaticLayout.Builder.obtain(text, where, where + after, getWidth()); } b.setText(text, where, where + after) @@ -292,7 +293,8 @@ public class DynamicLayout extends Layout .setSpacingMult(getSpacingMultiplier()) .setSpacingAdd(getSpacingAdd()) .setEllipsizedWidth(mEllipsizedWidth) - .setEllipsize(mEllipsizeAt); + .setEllipsize(mEllipsizeAt) + .setBreakStrategy(mBreakStrategy); reflowed.generate(b, false, true); int n = reflowed.getLineCount(); @@ -717,6 +719,7 @@ public class DynamicLayout extends Layout private boolean mEllipsize; private int mEllipsizedWidth; private TextUtils.TruncateAt mEllipsizeAt; + private int mBreakStrategy; private PackedIntVector mInts; private PackedObjectVector<Directions> mObjects; diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 22abb18..16ae5e2 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -16,6 +16,7 @@ package android.text; +import android.annotation.IntDef; import android.emoji.EmojiFactory; import android.graphics.Canvas; import android.graphics.Paint; @@ -33,6 +34,8 @@ import android.text.style.TabStopSpan; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; /** @@ -43,6 +46,31 @@ import java.util.Arrays; * For text that will not change, use a {@link StaticLayout}. */ public abstract class Layout { + /** @hide */ + @IntDef({BREAK_STRATEGY_SIMPLE, BREAK_STRATEGY_HIGH_QUALITY, BREAK_STRATEGY_BALANCED}) + @Retention(RetentionPolicy.SOURCE) + public @interface BreakStrategy {} + + /** + * Value for break strategy indicating simple line breaking. Automatic hyphens are not added + * (though soft hyphens are respected), and modifying text generally doesn't affect the layout + * before it (which yields a more consistent user experience when editing), but layout may not + * be the highest quality. + */ + public static final int BREAK_STRATEGY_SIMPLE = 0; + + /** + * Value for break strategy indicating high quality line breaking, including automatic + * hyphenation and doing whole-paragraph optimization of line breaks. + */ + public static final int BREAK_STRATEGY_HIGH_QUALITY = 1; + + /** + * Value for break strategy indicating balanced line breaking. The breaks are chosen to + * make all lines as close to the same length as possible, including automatic hyphenation. + */ + public static final int BREAK_STRATEGY_BALANCED = 2; + private static final ParagraphStyle[] NO_PARA_SPANS = ArrayUtils.emptyArray(ParagraphStyle.class); diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 4174df0..2bcb352 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -23,6 +23,7 @@ import android.text.style.LineHeightSpan; import android.text.style.MetricAffectingSpan; import android.text.style.TabStopSpan; import android.util.Log; +import android.util.Pools.SynchronizedPool; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; @@ -56,28 +57,23 @@ public class StaticLayout extends Layout { mNativePtr = nNewBuilder(); } - static Builder obtain() { - Builder b = null; - synchronized (sLock) { - for (int i = 0; i < sCached.length; i++) { - if (sCached[i] != null) { - b = sCached[i]; - sCached[i] = null; - break; - } - } - } + public static Builder obtain(CharSequence source, int start, int end, int width) { + Builder b = sPool.acquire(); if (b == null) { b = new Builder(); } // set default initial values - b.mWidth = 0; + b.mText = source; + b.mStart = start; + b.mEnd = end; + b.mWidth = width; + b.mAlignment = Alignment.ALIGN_NORMAL; b.mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR; b.mSpacingMult = 1.0f; b.mSpacingAdd = 0.0f; b.mIncludePad = true; - b.mEllipsizedWidth = 0; + b.mEllipsizedWidth = width; b.mEllipsize = null; b.mMaxLines = Integer.MAX_VALUE; @@ -85,18 +81,11 @@ public class StaticLayout extends Layout { return b; } - static void recycle(Builder b) { + private static void recycle(Builder b) { b.mPaint = null; b.mText = null; MeasuredText.recycle(b.mMeasuredText); - synchronized (sLock) { - for (int i = 0; i < sCached.length; i++) { - if (sCached[i] == null) { - sCached[i] = b; - break; - } - } - } + sPool.release(b); } // release any expensive state @@ -129,6 +118,11 @@ public class StaticLayout extends Layout { return this; } + public Builder setAlignment(Alignment alignment) { + mAlignment = alignment; + return this; + } + public Builder setTextDir(TextDirectionHeuristic textDir) { mTextDir = textDir; return this; @@ -166,6 +160,11 @@ public class StaticLayout extends Layout { return this; } + public Builder setBreakStrategy(@BreakStrategy int breakStrategy) { + mBreakStrategy = breakStrategy; + return this; + } + /** * Measurement and break iteration is done in native code. The protocol for using * the native code is as follows. @@ -207,10 +206,8 @@ public class StaticLayout extends Layout { } public StaticLayout build() { - // TODO: can optimize based on whether ellipsis is needed - StaticLayout result = new StaticLayout(mText); - result.generate(this, this.mIncludePad, this.mIncludePad); - recycle(this); + StaticLayout result = new StaticLayout(this); + Builder.recycle(this); return result; } @@ -230,6 +227,7 @@ public class StaticLayout extends Layout { int mEnd; TextPaint mPaint; int mWidth; + Alignment mAlignment; TextDirectionHeuristic mTextDir; float mSpacingMult; float mSpacingAdd; @@ -237,6 +235,7 @@ public class StaticLayout extends Layout { int mEllipsizedWidth; TextUtils.TruncateAt mEllipsize; int mMaxLines; + int mBreakStrategy; Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt(); @@ -245,8 +244,7 @@ public class StaticLayout extends Layout { Locale mLocale; - private static final Object sLock = new Object(); - private static final Builder[] sCached = new Builder[3]; + private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<Builder>(3); } public StaticLayout(CharSequence source, TextPaint paint, @@ -316,10 +314,9 @@ public class StaticLayout extends Layout { : new Ellipsizer(source), paint, outerwidth, align, textDir, spacingmult, spacingadd); - Builder b = Builder.obtain(); - b.setText(source, bufstart, bufend) + Builder b = Builder.obtain(source, bufstart, bufend, outerwidth) .setPaint(paint) - .setWidth(outerwidth) + .setAlignment(align) .setTextDir(textDir) .setSpacingMult(spacingmult) .setSpacingAdd(spacingadd) @@ -366,6 +363,35 @@ public class StaticLayout extends Layout { mLines = new int[mLineDirections.length]; } + private StaticLayout(Builder b) { + super((b.mEllipsize == null) + ? b.mText + : (b.mText instanceof Spanned) + ? new SpannedEllipsizer(b.mText) + : new Ellipsizer(b.mText), + b.mPaint, b.mWidth, b.mAlignment, b.mSpacingMult, b.mSpacingAdd); + + if (b.mEllipsize != null) { + Ellipsizer e = (Ellipsizer) getText(); + + e.mLayout = this; + e.mWidth = b.mEllipsizedWidth; + e.mMethod = b.mEllipsize; + mEllipsizedWidth = b.mEllipsizedWidth; + + mColumns = COLUMNS_ELLIPSIZE; + } else { + mColumns = COLUMNS_NORMAL; + mEllipsizedWidth = b.mWidth; + } + + mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns); + mLines = new int[mLineDirections.length]; + mMaximumVisibleLineCount = b.mMaxLines; + + generate(b, b.mIncludePad, b.mIncludePad); + } + /* package */ void generate(Builder b, boolean includepad, boolean trackpad) { CharSequence source = b.mText; int bufStart = b.mStart; @@ -477,10 +503,9 @@ public class StaticLayout extends Layout { } } - int breakStrategy = 0; // 0 = kBreakStrategy_Greedy nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart, firstWidth, firstWidthLineCount, restWidth, - variableTabStops, TAB_INCREMENT, breakStrategy); + variableTabStops, TAB_INCREMENT, b.mBreakStrategy); // measurement has to be done before performing line breaking // but we don't want to recompute fontmetrics or span ranges the diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 87f3e94..31c3fe8 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -5727,12 +5727,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be - resultSize = 0; + resultSize = size; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be - resultSize = 0; + resultSize = size; resultMode = MeasureSpec.UNSPECIFIED; } break; diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 77082b0..ec527d5 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -1562,7 +1562,7 @@ public class AccessibilityNodeInfo implements Parcelable { } /** - * Sets whether this node is visible to the user. + * Gets whether this node is visible to the user. * * @return Whether the node is visible to the user. */ diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java index e2f8efc..ec2528f 100644 --- a/core/java/android/widget/DayPickerView.java +++ b/core/java/android/widget/DayPickerView.java @@ -187,7 +187,6 @@ class DayPickerView extends ViewPager { * @param setSelected whether to set the specified day as selected */ private void setDate(long timeInMillis, boolean animate, boolean setSelected) { - // Set the selected day if (setSelected) { mSelectedDay.setTimeInMillis(timeInMillis); } @@ -196,6 +195,9 @@ class DayPickerView extends ViewPager { if (position != getCurrentItem()) { setCurrentItem(position, animate); } + + mTempCalendar.setTimeInMillis(timeInMillis); + mAdapter.setSelectedDay(mTempCalendar); } public long getDate() { diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index 349f3f0..a50941b 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -815,12 +815,12 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback mContext = context; mIntent = intent; - mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1); - - mLayoutInflater = LayoutInflater.from(context); if (mIntent == null) { throw new IllegalArgumentException("Non-null Intent must be specified."); } + + mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1); + mLayoutInflater = LayoutInflater.from(context); mRequestedViews = new RemoteViewsFrameLayoutRefSet(); // Strip the previously injected app widget id from service intent diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java index 3fb096c..d9f1f0e 100644 --- a/core/java/android/widget/SimpleMonthView.java +++ b/core/java/android/widget/SimpleMonthView.java @@ -80,11 +80,12 @@ class SimpleMonthView extends View { private final SimpleDateFormat mTitleFormatter; private final SimpleDateFormat mDayOfWeekFormatter; - private final int mMonthHeight; - private final int mDayOfWeekHeight; - private final int mDayHeight; - private final int mCellWidth; - private final int mDaySelectorRadius; + // Desired dimensions. + private final int mDesiredMonthHeight; + private final int mDesiredDayOfWeekHeight; + private final int mDesiredDayHeight; + private final int mDesiredCellWidth; + private final int mDesiredDaySelectorRadius; // Next/previous drawables. private final Drawable mPrevDrawable; @@ -99,6 +100,13 @@ class SimpleMonthView extends View { private int mMonth; private int mYear; + // Dimensions as laid out. + private int mMonthHeight; + private int mDayOfWeekHeight; + private int mDayHeight; + private int mCellWidth; + private int mDaySelectorRadius; + private int mPaddedWidth; private int mPaddedHeight; @@ -158,11 +166,11 @@ class SimpleMonthView extends View { super(context, attrs, defStyleAttr, defStyleRes); final Resources res = context.getResources(); - mMonthHeight = res.getDimensionPixelSize(R.dimen.date_picker_month_height); - mDayOfWeekHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_of_week_height); - mDayHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_height); - mCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width); - mDaySelectorRadius = res.getDimensionPixelSize(R.dimen.date_picker_day_selector_radius); + mDesiredMonthHeight = res.getDimensionPixelSize(R.dimen.date_picker_month_height); + mDesiredDayOfWeekHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_of_week_height); + mDesiredDayHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_height); + mDesiredCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width); + mDesiredDaySelectorRadius = res.getDimensionPixelSize(R.dimen.date_picker_day_selector_radius); mPrevDrawable = context.getDrawable(R.drawable.ic_chevron_left); mNextDrawable = context.getDrawable(R.drawable.ic_chevron_right); @@ -400,7 +408,7 @@ class SimpleMonthView extends View { final TextPaint p = mDayOfWeekPaint; final int headerHeight = mMonthHeight; final int rowHeight = mDayOfWeekHeight; - final int colWidth = mPaddedWidth / DAYS_IN_WEEK; + final int colWidth = mCellWidth; // Text is vertically centered within the day of week height. final float halfLineHeight = (p.ascent() + p.descent()) / 2f; @@ -426,7 +434,7 @@ class SimpleMonthView extends View { final TextPaint p = mDayPaint; final int headerHeight = mMonthHeight + mDayOfWeekHeight; final int rowHeight = mDayHeight; - final int colWidth = mPaddedWidth / DAYS_IN_WEEK; + final int colWidth = mCellWidth; // Text is vertically centered within the row height. final float halfLineHeight = (p.ascent() + p.descent()) / 2f; @@ -627,9 +635,9 @@ class SimpleMonthView extends View { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - final int preferredHeight = mDayHeight * mNumWeeks + mDayOfWeekHeight + mMonthHeight - + getPaddingTop() + getPaddingBottom(); - final int preferredWidth = mCellWidth * DAYS_IN_WEEK + final int preferredHeight = mDesiredDayHeight * mNumWeeks + mDesiredDayOfWeekHeight + + mDesiredMonthHeight + getPaddingTop() + getPaddingBottom(); + final int preferredWidth = mDesiredCellWidth * DAYS_IN_WEEK + getPaddingStart() + getPaddingEnd(); final int resolvedWidth = resolveSize(preferredWidth, widthMeasureSpec); final int resolvedHeight = resolveSize(preferredHeight, heightMeasureSpec); @@ -637,16 +645,46 @@ class SimpleMonthView extends View { } @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - final int paddedLeft = getPaddingLeft(); - final int paddedTop = getPaddingTop(); - final int paddedRight = w - getPaddingRight(); - final int paddedBottom = h - getPaddingBottom(); - mPaddedWidth = paddedRight - paddedLeft; - mPaddedHeight = paddedBottom - paddedTop; - - final int monthHeight = mMonthHeight; + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (!changed) { + return; + } + + // Let's initialize a completely reasonable number of variables. + final int w = right - left; + final int h = bottom - top; + final int paddingLeft = getPaddingLeft(); + final int paddingTop = getPaddingTop(); + final int paddingRight = getPaddingRight(); + final int paddingBottom = getPaddingBottom(); + final int paddedRight = w - paddingRight; + final int paddedBottom = h - paddingBottom; + final int paddedWidth = paddedRight - paddingLeft; + final int paddedHeight = paddedBottom - paddingTop; + if (paddedWidth == mPaddedWidth || paddedHeight == mPaddedHeight) { + return; + } + + mPaddedWidth = paddedWidth; + mPaddedHeight = paddedHeight; + + // We may have been laid out smaller than our preferred size. If so, + // scale all dimensions to fit. + final int measuredPaddedHeight = getMeasuredHeight() - paddingTop - paddingBottom; + final float scaleH = paddedHeight / (float) measuredPaddedHeight; + final int monthHeight = (int) (mDesiredMonthHeight * scaleH); final int cellWidth = mPaddedWidth / DAYS_IN_WEEK; + mMonthHeight = monthHeight; + mDayOfWeekHeight = (int) (mDesiredDayOfWeekHeight * scaleH); + mDayHeight = (int) (mDesiredDayHeight * scaleH); + mCellWidth = cellWidth; + + // Compute the largest day selector radius that's still within the clip + // bounds and desired selector radius. + final int maxSelectorWidth = cellWidth / 2 + Math.min(paddingLeft, paddingRight); + final int maxSelectorHeight = mDayHeight / 2 + paddingBottom; + mDaySelectorRadius = Math.min(mDesiredDaySelectorRadius, + Math.min(maxSelectorWidth, maxSelectorHeight)); // Vertically center the previous/next drawables within the month // header, horizontally center within the day cell, then expand the @@ -660,7 +698,7 @@ class SimpleMonthView extends View { // Button bounds don't include padding, but hit area does. prevDrawable.setBounds(iconLeft, iconTop, iconLeft + dW, iconTop + dH); - mPrevHitArea.set(0, 0, paddedLeft + cellWidth, paddedTop + monthHeight); + mPrevHitArea.set(0, 0, paddingLeft + cellWidth, paddingTop + monthHeight); } final Drawable nextDrawable = mNextDrawable; @@ -668,11 +706,11 @@ class SimpleMonthView extends View { final int dW = nextDrawable.getIntrinsicWidth(); final int dH = nextDrawable.getIntrinsicHeight(); final int iconTop = (monthHeight - dH) / 2; - final int iconRight = mPaddedWidth - (cellWidth - dW) / 2; + final int iconRight = paddedWidth - (cellWidth - dW) / 2; // Button bounds don't include padding, but hit area does. nextDrawable.setBounds(iconRight - dW, iconTop, iconRight, iconTop + dH); - mNextHitArea.set(paddedRight - cellWidth, 0, w, paddedTop + monthHeight); + mNextHitArea.set(paddedRight - cellWidth, 0, w, paddingTop + monthHeight); } // Invalidate cached accessibility information. @@ -753,7 +791,7 @@ class SimpleMonthView extends View { // Compute left edge. final int col = index % DAYS_IN_WEEK; - final int colWidth = mPaddedWidth / DAYS_IN_WEEK; + final int colWidth = mCellWidth; final int left = getPaddingLeft() + col * colWidth; // Compute top edge. diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 718ef93..2723080 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -543,6 +543,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private float mSpacingMult = 1.0f; private float mSpacingAdd = 0.0f; + private int mBreakStrategy; + private int mMaximum = Integer.MAX_VALUE; private int mMaxMode = LINES; private int mMinimum = 0; @@ -680,6 +682,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boolean elegant = false; float letterSpacing = 0; String fontFeatureSettings = null; + mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; final Resources.Theme theme = context.getTheme(); @@ -1133,6 +1136,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextView_fontFeatureSettings: fontFeatureSettings = a.getString(attr); break; + + case com.android.internal.R.styleable.TextView_breakStrategy: + mBreakStrategy = a.getInt(attr, Layout.BREAK_STRATEGY_SIMPLE); } } a.recycle(); @@ -2960,6 +2966,35 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Sets the break strategy for breaking paragraphs into lines. The default value for + * TextView is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}, and the default value for + * EditText is {@link Layout#BREAK_STRATEGY_SIMPLE}, the latter to avoid the + * text "dancing" when being edited. + * + * @attr ref android.R.styleable#TextView_breakStrategy + * @see #getBreakStrategy() + */ + public void setBreakStrategy(@Layout.BreakStrategy int breakStrategy) { + mBreakStrategy = breakStrategy; + if (mLayout != null) { + nullLayouts(); + requestLayout(); + invalidate(); + } + } + + /** + * @return the currently set break strategy. + * + * @attr ref android.R.styleable#TextView_breakStrategy + * @see #setBreakStrategy(int) + */ + @Layout.BreakStrategy + public int getBreakStrategy() { + return mBreakStrategy; + } + + /** * Sets font feature settings. The format is the same as the CSS * font-feature-settings attribute: * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings @@ -6492,27 +6527,25 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener hintBoring, mIncludePad, mEllipsize, ellipsisWidth); } - } else if (shouldEllipsize) { - mHintLayout = new StaticLayout(mHint, - 0, mHint.length(), - mTextPaint, hintWidth, alignment, mTextDir, mSpacingMult, - mSpacingAdd, mIncludePad, mEllipsize, - ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); - } else { - mHintLayout = new StaticLayout(mHint, mTextPaint, - hintWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd, - mIncludePad); } - } else if (shouldEllipsize) { - mHintLayout = new StaticLayout(mHint, - 0, mHint.length(), - mTextPaint, hintWidth, alignment, mTextDir, mSpacingMult, - mSpacingAdd, mIncludePad, mEllipsize, - ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); - } else { - mHintLayout = new StaticLayout(mHint, mTextPaint, - hintWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd, - mIncludePad); + } + // TODO: code duplication with makeSingleLayout() + if (mHintLayout == null) { + StaticLayout.Builder builder = StaticLayout.Builder.obtain(mHint, 0, + mHint.length(), hintWidth) + .setPaint(mTextPaint) + .setAlignment(alignment) + .setTextDir(mTextDir) + .setSpacingMult(mSpacingMult) + .setSpacingAdd(mSpacingAdd) + .setIncludePad(mIncludePad) + .setBreakStrategy(mBreakStrategy); + if (shouldEllipsize) { + builder.setEllipsize(mEllipsize) + .setEllipsizedWidth(ellipsisWidth) + .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); + } + mHintLayout = builder.build(); } } @@ -6544,9 +6577,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Layout result = null; if (mText instanceof Spannable) { result = new DynamicLayout(mText, mTransformed, mTextPaint, wantWidth, - alignment, mTextDir, mSpacingMult, - mSpacingAdd, mIncludePad, getKeyListener() == null ? effectiveEllipsize : null, - ellipsisWidth); + alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad, mBreakStrategy, + getKeyListener() == null ? effectiveEllipsize : null, ellipsisWidth); } else { if (boring == UNKNOWN_BORING) { boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring); @@ -6583,29 +6615,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boring, mIncludePad, effectiveEllipsize, ellipsisWidth); } - } else if (shouldEllipsize) { - result = new StaticLayout(mTransformed, - 0, mTransformed.length(), - mTextPaint, wantWidth, alignment, mTextDir, mSpacingMult, - mSpacingAdd, mIncludePad, effectiveEllipsize, - ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); - } else { - result = new StaticLayout(mTransformed, mTextPaint, - wantWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd, - mIncludePad); } - } else if (shouldEllipsize) { - result = new StaticLayout(mTransformed, - 0, mTransformed.length(), - mTextPaint, wantWidth, alignment, mTextDir, mSpacingMult, - mSpacingAdd, mIncludePad, effectiveEllipsize, - ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); - } else { - result = new StaticLayout(mTransformed, mTextPaint, - wantWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd, - mIncludePad); } } + if (result == null) { + StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed, + 0, mTransformed.length(), wantWidth) + .setPaint(mTextPaint) + .setAlignment(alignment) + .setTextDir(mTextDir) + .setSpacingMult(mSpacingMult) + .setSpacingAdd(mSpacingAdd) + .setIncludePad(mIncludePad) + .setBreakStrategy(mBreakStrategy); + if (shouldEllipsize) { + builder.setEllipsize(effectiveEllipsize) + .setEllipsizedWidth(ellipsisWidth) + .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); + } + // TODO: explore always setting maxLines + result = builder.build(); + } return result; } diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java index c58d5cb..2365b48 100644 --- a/core/java/android/widget/TimePickerClockDelegate.java +++ b/core/java/android/widget/TimePickerClockDelegate.java @@ -25,8 +25,10 @@ import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; +import android.text.SpannableStringBuilder; import android.text.format.DateFormat; import android.text.format.DateUtils; +import android.text.style.TtsSpan; import android.util.AttributeSet; import android.util.Log; import android.util.StateSet; @@ -155,13 +157,16 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl mHourView.setMinWidth(computeStableWidth(mHourView, 24)); mMinuteView.setMinWidth(computeStableWidth(mMinuteView, 60)); + final SpannableStringBuilder amLabel = new SpannableStringBuilder() + .append(amPmStrings[0], new TtsSpan.VerbatimBuilder(amPmStrings[0]).build(), 0); + // Set up AM/PM labels. mAmPmLayout = mainView.findViewById(R.id.ampm_layout); mAmLabel = (CheckedTextView) mAmPmLayout.findViewById(R.id.am_label); - mAmLabel.setText(amPmStrings[0]); + mAmLabel.setText(obtainVerbatim(amPmStrings[0])); mAmLabel.setOnClickListener(mClickListener); mPmLabel = (CheckedTextView) mAmPmLayout.findViewById(R.id.pm_label); - mPmLabel.setText(amPmStrings[1]); + mPmLabel.setText(obtainVerbatim(amPmStrings[1])); mPmLabel.setOnClickListener(mClickListener); // For the sake of backwards compatibility, attempt to extract the text @@ -220,6 +225,11 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl initialize(currentHour, currentMinute, false /* 12h */, HOUR_INDEX); } + private static final CharSequence obtainVerbatim(String text) { + return new SpannableStringBuilder().append(text, + new TtsSpan.VerbatimBuilder(text).build(), 0); + } + /** * The legacy text color might have been poorly defined. Ensures that it * has an appropriate activated state, using the selected state if one |
