diff options
Diffstat (limited to 'core/java/android')
-rw-r--r-- | core/java/android/app/UiModeManager.java | 7 | ||||
-rw-r--r-- | core/java/android/app/admin/DeviceAdminReceiver.java | 13 | ||||
-rw-r--r-- | core/java/android/content/Context.java | 15 | ||||
-rw-r--r-- | core/java/android/hardware/Sensor.java | 92 | ||||
-rw-r--r-- | core/java/android/net/ConnectivityManager.java | 4 | ||||
-rw-r--r-- | core/java/android/provider/Settings.java | 2 | ||||
-rw-r--r-- | core/java/android/text/SpannableStringBuilder.java | 550 | ||||
-rw-r--r-- | core/java/android/text/format/DateUtils.java | 154 | ||||
-rw-r--r-- | core/java/android/transition/ArcMotion.java | 3 | ||||
-rw-r--r-- | core/java/android/transition/CircularPropagation.java | 15 | ||||
-rw-r--r-- | core/java/android/transition/Explode.java | 24 | ||||
-rw-r--r-- | core/java/android/transition/PatternPathMotion.java | 12 | ||||
-rw-r--r-- | core/java/android/transition/SidePropagation.java | 1 | ||||
-rw-r--r-- | core/java/android/widget/EdgeEffect.java | 5 |
14 files changed, 519 insertions, 378 deletions
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 0a255f7..0f6ce12 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -219,10 +219,9 @@ public class UiModeManager { } /** - * Returns the currently configured night mode. - * - * @return {@link #MODE_NIGHT_NO}, {@link #MODE_NIGHT_YES}, or - * {@link #MODE_NIGHT_AUTO}. When an error occurred -1 is returned. + * @return the currently configured night mode. May be one of + * {@link #MODE_NIGHT_NO}, {@link #MODE_NIGHT_YES}, + * {@link #MODE_NIGHT_AUTO}, or -1 on error. */ public int getNightMode() { if (mService != null) { diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index e9cce51..381d851 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -170,10 +170,10 @@ public class DeviceAdminReceiver extends BroadcastReceiver { * lock task mode from an authorized package. The extra {@link #EXTRA_LOCK_TASK_PACKAGE} * will describe the authorized package using lock task mode. * - * @see DevicePolicyManager#isLockTaskPermitted(String) - * * <p>The calling device admin must be the device owner or profile * owner to receive this broadcast. + * + * @see DevicePolicyManager#isLockTaskPermitted(String) */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LOCK_TASK_ENTERING @@ -183,20 +183,19 @@ public class DeviceAdminReceiver extends BroadcastReceiver { * Action sent to a device administrator to notify that the device is exiting * lock task mode from an authorized package. * - * @see DevicePolicyManager#isLockTaskPermitted(String) - * * <p>The calling device admin must be the device owner or profile * owner to receive this broadcast. + * + * @see DevicePolicyManager#isLockTaskPermitted(String) */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LOCK_TASK_EXITING = "android.app.action.LOCK_TASK_EXITING"; /** - * A boolean describing whether the device is currently entering or exiting - * lock task mode. + * A string containing the name of the package entering lock task mode. * - * @see #ACTION_LOCK_TASK_CHANGED + * @see #ACTION_LOCK_TASK_ENTERING */ public static final String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE"; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index dec2524..df620d0 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2149,17 +2149,21 @@ public abstract class Context { WIFI_PASSPOINT_SERVICE, WIFI_P2P_SERVICE, WIFI_SCANNING_SERVICE, + //@hide: WIFI_RTT_SERVICE, //@hide: ETHERNET_SERVICE, WIFI_RTT_SERVICE, NSD_SERVICE, AUDIO_SERVICE, + //@hide: FINGERPRINT_SERVICE, MEDIA_ROUTER_SERVICE, TELEPHONY_SERVICE, + TELEPHONY_SUBSCRIPTION_SERVICE, TELECOM_SERVICE, CLIPBOARD_SERVICE, INPUT_METHOD_SERVICE, TEXT_SERVICES_MANAGER_SERVICE, APPWIDGET_SERVICE, + //@hide: VOICE_INTERACTION_MANAGER_SERVICE, //@hide: BACKUP_SERVICE, DROPBOX_SERVICE, DEVICE_POLICY_SERVICE, @@ -2171,16 +2175,23 @@ public abstract class Context { USB_SERVICE, LAUNCHER_APPS_SERVICE, //@hide: SERIAL_SERVICE, + //@hide: HDMI_CONTROL_SERVICE, INPUT_SERVICE, DISPLAY_SERVICE, - //@hide: SCHEDULING_POLICY_SERVICE, USER_SERVICE, - //@hide: APP_OPS_SERVICE + RESTRICTIONS_SERVICE, + APP_OPS_SERVICE, CAMERA_SERVICE, PRINT_SERVICE, + CONSUMER_IR_SERVICE, + //@hide: TRUST_SERVICE, + TV_INPUT_SERVICE, + //@hide: NETWORK_SCORE_SERVICE, + USAGE_STATS_SERVICE, MEDIA_SESSION_SERVICE, BATTERY_SERVICE, JOB_SCHEDULER_SERVICE, + //@hide: PERSISTENT_DATA_BLOCK_SERVICE, MEDIA_PROJECTION_SERVICE, MIDI_SERVICE, }) diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index fa5e9d2..39f4cca 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -833,4 +833,96 @@ public final class Sensor { + ", type=" + mType + ", maxRange=" + mMaxRange + ", resolution=" + mResolution + ", power=" + mPower + ", minDelay=" + mMinDelay + "}"; } + + /** + * Sets the Type associated with the sensor. + * NOTE: to be used only by native bindings in SensorManager. + * + * This allows interned static strings to be used across all representations of the Sensor. If + * a sensor type is not referenced here, it will still be interned by the native SensorManager. + * + * @return {@code true} if the StringType was successfully set, {@code false} otherwise. + */ + private boolean setType(int value) { + mType = value; + switch (mType) { + case TYPE_ACCELEROMETER: + mStringType = STRING_TYPE_ACCELEROMETER; + return true; + case TYPE_AMBIENT_TEMPERATURE: + mStringType = STRING_TYPE_AMBIENT_TEMPERATURE; + return true; + case TYPE_GAME_ROTATION_VECTOR: + mStringType = STRING_TYPE_GAME_ROTATION_VECTOR; + return true; + case TYPE_GEOMAGNETIC_ROTATION_VECTOR: + mStringType = STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR; + return true; + case TYPE_GLANCE_GESTURE: + mStringType = STRING_TYPE_GLANCE_GESTURE; + return true; + case TYPE_GRAVITY: + mStringType = STRING_TYPE_GRAVITY; + return true; + case TYPE_GYROSCOPE: + mStringType = STRING_TYPE_GYROSCOPE; + return true; + case TYPE_GYROSCOPE_UNCALIBRATED: + mStringType = STRING_TYPE_GYROSCOPE_UNCALIBRATED; + return true; + case TYPE_HEART_RATE: + mStringType = STRING_TYPE_HEART_RATE; + return true; + case TYPE_LIGHT: + mStringType = STRING_TYPE_LIGHT; + return true; + case TYPE_LINEAR_ACCELERATION: + mStringType = STRING_TYPE_LINEAR_ACCELERATION; + return true; + case TYPE_MAGNETIC_FIELD: + mStringType = STRING_TYPE_MAGNETIC_FIELD; + return true; + case TYPE_MAGNETIC_FIELD_UNCALIBRATED: + mStringType = STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED; + return true; + case TYPE_PICK_UP_GESTURE: + mStringType = STRING_TYPE_PICK_UP_GESTURE; + return true; + case TYPE_PRESSURE: + mStringType = STRING_TYPE_PRESSURE; + return true; + case TYPE_PROXIMITY: + mStringType = STRING_TYPE_PROXIMITY; + return true; + case TYPE_RELATIVE_HUMIDITY: + mStringType = STRING_TYPE_RELATIVE_HUMIDITY; + return true; + case TYPE_ROTATION_VECTOR: + mStringType = STRING_TYPE_ROTATION_VECTOR; + return true; + case TYPE_SIGNIFICANT_MOTION: + mStringType = STRING_TYPE_SIGNIFICANT_MOTION; + return true; + case TYPE_STEP_COUNTER: + mStringType = STRING_TYPE_STEP_COUNTER; + return true; + case TYPE_STEP_DETECTOR: + mStringType = STRING_TYPE_STEP_DETECTOR; + return true; + case TYPE_TILT_DETECTOR: + mStringType = SENSOR_STRING_TYPE_TILT_DETECTOR; + return true; + case TYPE_WAKE_GESTURE: + mStringType = STRING_TYPE_WAKE_GESTURE; + return true; + case TYPE_ORIENTATION: + mStringType = STRING_TYPE_ORIENTATION; + return true; + case TYPE_TEMPERATURE: + mStringType = STRING_TYPE_TEMPERATURE; + return true; + default: + return false; + } + } } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 1a51808..808be21 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -2371,6 +2371,10 @@ public class ConnectivityManager { * The lookup key for a {@link Network} object included with the intent after * successfully finding a network for the applications request. Retrieve it with * {@link android.content.Intent#getParcelableExtra(String)}. + * <p> + * Note that if you intend to invoke (@link #setProcessDefaultNetwork(Network)) or + * {@link Network#openConnection(java.net.URL)} then you must get a + * ConnectivityManager instance before doing so. */ public static final String EXTRA_NETWORK = "android.net.extra.NETWORK"; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 7b3eceb..c836a33 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2098,7 +2098,7 @@ public final class Settings { @Override public boolean validate(String value) { // TODO: No idea what the correct format is. - return value == null || value.length() > MAX_LENGTH; + return value == null || value.length() < MAX_LENGTH; } }; diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index 06df683..7ce44e1 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -26,6 +26,7 @@ import com.android.internal.util.GrowingArrayUtils; import libcore.util.EmptyArray; import java.lang.reflect.Array; +import java.util.IdentityHashMap; /** * This is the class for text whose content and markup can both be changed. @@ -68,6 +69,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable mSpanStarts = EmptyArray.INT; mSpanEnds = EmptyArray.INT; mSpanFlags = EmptyArray.INT; + mSpanMax = EmptyArray.INT; if (text instanceof Spanned) { Spanned sp = (Spanned) text; @@ -94,6 +96,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable setSpan(false, spans[i], st, en, fl); } + restoreInvariants(); } } @@ -147,9 +150,12 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable if (mGapLength < 1) new Exception("mGapLength < 1").printStackTrace(); - for (int i = 0; i < mSpanCount; i++) { - if (mSpanStarts[i] > mGapStart) mSpanStarts[i] += delta; - if (mSpanEnds[i] > mGapStart) mSpanEnds[i] += delta; + if (mSpanCount != 0) { + for (int i = 0; i < mSpanCount; i++) { + if (mSpanStarts[i] > mGapStart) mSpanStarts[i] += delta; + if (mSpanEnds[i] > mGapStart) mSpanEnds[i] += delta; + } + calcMax(treeRoot()); } } @@ -167,35 +173,38 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable System.arraycopy(mText, where + mGapLength - overlap, mText, mGapStart, overlap); } - // XXX be more clever - for (int i = 0; i < mSpanCount; i++) { - int start = mSpanStarts[i]; - int end = mSpanEnds[i]; - - if (start > mGapStart) - start -= mGapLength; - if (start > where) - start += mGapLength; - else if (start == where) { - int flag = (mSpanFlags[i] & START_MASK) >> START_SHIFT; + // TODO: be more clever (although the win really isn't that big) + if (mSpanCount != 0) { + for (int i = 0; i < mSpanCount; i++) { + int start = mSpanStarts[i]; + int end = mSpanEnds[i]; - if (flag == POINT || (atEnd && flag == PARAGRAPH)) + if (start > mGapStart) + start -= mGapLength; + if (start > where) start += mGapLength; - } + else if (start == where) { + int flag = (mSpanFlags[i] & START_MASK) >> START_SHIFT; - if (end > mGapStart) - end -= mGapLength; - if (end > where) - end += mGapLength; - else if (end == where) { - int flag = (mSpanFlags[i] & END_MASK); + if (flag == POINT || (atEnd && flag == PARAGRAPH)) + start += mGapLength; + } - if (flag == POINT || (atEnd && flag == PARAGRAPH)) + if (end > mGapStart) + end -= mGapLength; + if (end > where) end += mGapLength; - } + else if (end == where) { + int flag = (mSpanFlags[i] & END_MASK); + + if (flag == POINT || (atEnd && flag == PARAGRAPH)) + end += mGapLength; + } - mSpanStarts[i] = start; - mSpanEnds[i] = end; + mSpanStarts[i] = start; + mSpanEnds[i] = end; + } + calcMax(treeRoot()); } mGapStart = where; @@ -243,6 +252,9 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable sendSpanRemoved(what, ostart, oend); } + if (mIndexOfSpan != null) { + mIndexOfSpan.clear(); + } } // Documentation from interface @@ -277,12 +289,39 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable return append(String.valueOf(text)); } + // Returns true if a node was removed (so we can restart search from root) + private boolean removeSpansForChange(int start, int end, boolean textIsRemoved, int i) { + if ((i & 1) != 0) { + // internal tree node + if (resolveGap(mSpanMax[i]) >= start && + removeSpansForChange(start, end, textIsRemoved, leftChild(i))) { + return true; + } + } + if (i < mSpanCount) { + if ((mSpanFlags[i] & Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) == + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE && + mSpanStarts[i] >= start && mSpanStarts[i] < mGapStart + mGapLength && + mSpanEnds[i] >= start && mSpanEnds[i] < mGapStart + mGapLength && + // The following condition indicates that the span would become empty + (textIsRemoved || mSpanStarts[i] > start || mSpanEnds[i] < mGapStart)) { + mIndexOfSpan.remove(mSpans[i]); + removeSpan(i); + return true; + } + return resolveGap(mSpanStarts[i]) <= end && (i & 1) != 0 && + removeSpansForChange(start, end, textIsRemoved, rightChild(i)); + } + return false; + } + private void change(int start, int end, CharSequence cs, int csStart, int csEnd) { // Can be negative final int replacedLength = end - start; final int replacementLength = csEnd - csStart; final int nbNewChars = replacementLength - replacedLength; + boolean changed = false; for (int i = mSpanCount - 1; i >= 0; i--) { int spanStart = mSpanStarts[i]; if (spanStart > mGapStart) @@ -309,8 +348,10 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable break; } - if (spanStart != ost || spanEnd != oen) + if (spanStart != ost || spanEnd != oen) { setSpan(false, mSpans[i], spanStart, spanEnd, mSpanFlags[i]); + changed = true; + } } int flags = 0; @@ -320,6 +361,9 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable else if (spanEnd == end + nbNewChars) flags |= SPAN_END_AT_END; mSpanFlags[i] |= flags; } + if (changed) { + restoreInvariants(); + } moveGapTo(end); @@ -331,23 +375,10 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable // The removal pass needs to be done before the gap is updated in order to broadcast the // correct previous positions to the correct intersecting SpanWatchers if (replacedLength > 0) { // no need for span fixup on pure insertion - // A for loop will not work because the array is being modified - // Do not iterate in reverse to keep the SpanWatchers notified in ordering - // Also, a removed SpanWatcher should not get notified of removed spans located - // further in the span array. - int i = 0; - while (i < mSpanCount) { - if ((mSpanFlags[i] & Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) == - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE && - mSpanStarts[i] >= start && mSpanStarts[i] < mGapStart + mGapLength && - mSpanEnds[i] >= start && mSpanEnds[i] < mGapStart + mGapLength && - // This condition indicates that the span would become empty - (textIsRemoved || mSpanStarts[i] > start || mSpanEnds[i] < mGapStart)) { - removeSpan(i); - continue; // do not increment i, spans will be shifted left in the array - } - - i++; + while (mSpanCount > 0 && + removeSpansForChange(start, end, textIsRemoved, treeRoot())) { + // keep deleting spans as needed, and restart from root after every deletion + // because deletion can invalidate an index. } } @@ -360,6 +391,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable TextUtils.getChars(cs, csStart, csEnd, mText, start); if (replacedLength > 0) { // no need for span fixup on pure insertion + // TODO potential optimization: only update bounds on intersecting spans final boolean atEnd = (mGapStart + mGapLength == mText.length); for (int i = 0; i < mSpanCount; i++) { @@ -371,10 +403,10 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable mSpanEnds[i] = updatedIntervalBound(mSpanEnds[i], start, nbNewChars, endFlag, atEnd, textIsRemoved); } + // TODO potential optimization: only fix up invariants when bounds actually changed + restoreInvariants(); } - mSpanCountBeforeAdd = mSpanCount; - if (cs instanceof Spanned) { Spanned sp = (Spanned) cs; Object[] spans = sp.getSpans(csStart, csEnd, Object.class); @@ -389,9 +421,10 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable // Add span only if this object is not yet used as a span in this string if (getSpanStart(spans[i]) < 0) { setSpan(false, spans[i], st - csStart + start, en - csStart + start, - sp.getSpanFlags(spans[i])); + sp.getSpanFlags(spans[i]) | SPAN_ADDED); } } + restoreInvariants(); } } @@ -427,6 +460,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable return offset; } + // Note: caller is responsible for removing the mIndexOfSpan entry. private void removeSpan(int i) { Object object = mSpans[i]; @@ -444,8 +478,12 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable mSpanCount--; + invalidateIndex(i); mSpans[mSpanCount] = null; + // Invariants must be restored before sending span removed notifications. + restoreInvariants(); + sendSpanRemoved(object, start, end); } @@ -496,10 +534,12 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable change(start, end, tb, tbstart, tbend); if (adjustSelection) { + boolean changed = false; if (selectionStart > start && selectionStart < end) { final int offset = (selectionStart - start) * newLen / origLen; selectionStart = start + offset; + changed = true; setSpan(false, Selection.SELECTION_START, selectionStart, selectionStart, Spanned.SPAN_POINT_POINT); } @@ -507,9 +547,13 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable final int offset = (selectionEnd - start) * newLen / origLen; selectionEnd = start + offset; + changed = true; setSpan(false, Selection.SELECTION_END, selectionEnd, selectionEnd, Spanned.SPAN_POINT_POINT); } + if (changed) { + restoreInvariants(); + } } sendTextChanged(textWatchers, start, origLen, newLen); @@ -536,12 +580,15 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable } private void sendToSpanWatchers(int replaceStart, int replaceEnd, int nbNewChars) { - for (int i = 0; i < mSpanCountBeforeAdd; i++) { + for (int i = 0; i < mSpanCount; i++) { + int spanFlags = mSpanFlags[i]; + + // This loop handles only modified (not added) spans. + if ((spanFlags & SPAN_ADDED) != 0) continue; int spanStart = mSpanStarts[i]; int spanEnd = mSpanEnds[i]; if (spanStart > mGapStart) spanStart -= mGapLength; if (spanEnd > mGapStart) spanEnd -= mGapLength; - int spanFlags = mSpanFlags[i]; int newReplaceEnd = replaceEnd + nbNewChars; boolean spanChanged = false; @@ -588,13 +635,17 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable mSpanFlags[i] &= ~SPAN_START_END_MASK; } - // The spans starting at mIntermediateSpanCount were added from the replacement text - for (int i = mSpanCountBeforeAdd; i < mSpanCount; i++) { - int spanStart = mSpanStarts[i]; - int spanEnd = mSpanEnds[i]; - if (spanStart > mGapStart) spanStart -= mGapLength; - if (spanEnd > mGapStart) spanEnd -= mGapLength; - sendSpanAdded(mSpans[i], spanStart, spanEnd); + // Handle added spans + for (int i = 0; i < mSpanCount; i++) { + int spanFlags = mSpanFlags[i]; + if ((spanFlags & SPAN_ADDED) != 0) { + mSpanFlags[i] &= ~SPAN_ADDED; + int spanStart = mSpanStarts[i]; + int spanEnd = mSpanEnds[i]; + if (spanStart > mGapStart) spanStart -= mGapLength; + if (spanEnd > mGapStart) spanEnd -= mGapLength; + sendSpanAdded(mSpans[i], spanStart, spanEnd); + } } } @@ -607,6 +658,9 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable setSpan(true, what, start, end, flags); } + // Note: if send is false, then it is the caller's responsibility to restore + // invariants. If send is false and the span already exists, then this method + // will not change the index of any spans. private void setSpan(boolean send, Object what, int start, int end, int flags) { checkRange("setSpan", start, end); @@ -661,8 +715,10 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable int count = mSpanCount; Object[] spans = mSpans; - for (int i = 0; i < count; i++) { - if (spans[i] == what) { + if (mIndexOfSpan != null) { + Integer index = mIndexOfSpan.get(what); + if (index != null) { + int i = index; int ostart = mSpanStarts[i]; int oend = mSpanEnds[i]; @@ -675,7 +731,10 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable mSpanEnds[i] = end; mSpanFlags[i] = flags; - if (send) sendSpanChanged(what, ostart, oend, nstart, nend); + if (send) { + restoreInvariants(); + sendSpanChanged(what, ostart, oend, nstart, nend); + } return; } @@ -685,43 +744,48 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable mSpanStarts = GrowingArrayUtils.append(mSpanStarts, mSpanCount, start); mSpanEnds = GrowingArrayUtils.append(mSpanEnds, mSpanCount, end); mSpanFlags = GrowingArrayUtils.append(mSpanFlags, mSpanCount, flags); + invalidateIndex(mSpanCount); mSpanCount++; + // Make sure there is enough room for empty interior nodes. + // This magic formula computes the size of the smallest perfect binary + // tree no smaller than mSpanCount. + int sizeOfMax = 2 * treeRoot() + 1; + if (mSpanMax.length < sizeOfMax) { + mSpanMax = new int[sizeOfMax]; + } - if (send) sendSpanAdded(what, nstart, nend); + if (send) { + restoreInvariants(); + sendSpanAdded(what, nstart, nend); + } } /** * Remove the specified markup object from the buffer. */ public void removeSpan(Object what) { - for (int i = mSpanCount - 1; i >= 0; i--) { - if (mSpans[i] == what) { - removeSpan(i); - return; - } + if (mIndexOfSpan == null) return; + Integer i = mIndexOfSpan.remove(what); + if (i != null) { + removeSpan(i.intValue()); } } /** + * Return externally visible offset given offset into gapped buffer. + */ + private int resolveGap(int i) { + return i > mGapStart ? i - mGapLength : i; + } + + /** * Return the buffer offset of the beginning of the specified * markup object, or -1 if it is not attached to this buffer. */ public int getSpanStart(Object what) { - int count = mSpanCount; - Object[] spans = mSpans; - - for (int i = count - 1; i >= 0; i--) { - if (spans[i] == what) { - int where = mSpanStarts[i]; - - if (where > mGapStart) - where -= mGapLength; - - return where; - } - } - - return -1; + if (mIndexOfSpan == null) return -1; + Integer i = mIndexOfSpan.get(what); + return i == null ? -1 : resolveGap(mSpanStarts[i]); } /** @@ -729,21 +793,9 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable * markup object, or -1 if it is not attached to this buffer. */ public int getSpanEnd(Object what) { - int count = mSpanCount; - Object[] spans = mSpans; - - for (int i = count - 1; i >= 0; i--) { - if (spans[i] == what) { - int where = mSpanEnds[i]; - - if (where > mGapStart) - where -= mGapLength; - - return where; - } - } - - return -1; + if (mIndexOfSpan == null) return -1; + Integer i = mIndexOfSpan.get(what); + return i == null ? -1 : resolveGap(mSpanEnds[i]); } /** @@ -751,16 +803,9 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable * markup object, or 0 if it is not attached to this buffer. */ public int getSpanFlags(Object what) { - int count = mSpanCount; - Object[] spans = mSpans; - - for (int i = count - 1; i >= 0; i--) { - if (spans[i] == what) { - return mSpanFlags[i]; - } - } - - return 0; + if (mIndexOfSpan == null) return 0; + Integer i = mIndexOfSpan.get(what); + return i == null ? 0 : mSpanFlags[i]; } /** @@ -770,59 +815,84 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable */ @SuppressWarnings("unchecked") public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) { - if (kind == null) return ArrayUtils.emptyArray(kind); + if (kind == null || mSpanCount == 0) return ArrayUtils.emptyArray(kind); + int count = countSpans(queryStart, queryEnd, kind, treeRoot()); + if (count == 0) { + return ArrayUtils.emptyArray(kind); + } - int spanCount = mSpanCount; - Object[] spans = mSpans; - int[] starts = mSpanStarts; - int[] ends = mSpanEnds; - int[] flags = mSpanFlags; - int gapstart = mGapStart; - int gaplen = mGapLength; + // Safe conversion, but requires a suppressWarning + T[] ret = (T[]) Array.newInstance(kind, count); + getSpansRec(queryStart, queryEnd, kind, treeRoot(), ret, 0); + return ret; + } + private int countSpans(int queryStart, int queryEnd, Class kind, int i) { int count = 0; - T[] ret = null; - T ret1 = null; - - for (int i = 0; i < spanCount; i++) { - int spanStart = starts[i]; - if (spanStart > gapstart) { - spanStart -= gaplen; + if ((i & 1) != 0) { + // internal tree node + int left = leftChild(i); + int spanMax = mSpanMax[left]; + if (spanMax > mGapStart) { + spanMax -= mGapLength; } - if (spanStart > queryEnd) { - continue; + if (spanMax >= queryStart) { + count = countSpans(queryStart, queryEnd, kind, left); } - - int spanEnd = ends[i]; - if (spanEnd > gapstart) { - spanEnd -= gaplen; + } + if (i < mSpanCount) { + int spanStart = mSpanStarts[i]; + if (spanStart > mGapStart) { + spanStart -= mGapLength; } - if (spanEnd < queryStart) { - continue; + if (spanStart <= queryEnd) { + int spanEnd = mSpanEnds[i]; + if (spanEnd > mGapStart) { + spanEnd -= mGapLength; + } + if (spanEnd >= queryStart && + (spanStart == spanEnd || queryStart == queryEnd || + (spanStart != queryEnd && spanEnd != queryStart)) && + kind.isInstance(mSpans[i])) { + count++; + } + if ((i & 1) != 0) { + count += countSpans(queryStart, queryEnd, kind, rightChild(i)); + } } + } + return count; + } - if (spanStart != spanEnd && queryStart != queryEnd) { - if (spanStart == queryEnd) - continue; - if (spanEnd == queryStart) - continue; + @SuppressWarnings("unchecked") + private <T> int getSpansRec(int queryStart, int queryEnd, Class<T> kind, + int i, T[] ret, int count) { + if ((i & 1) != 0) { + // internal tree node + int left = leftChild(i); + int spanMax = mSpanMax[left]; + if (spanMax > mGapStart) { + spanMax -= mGapLength; } - - // Expensive test, should be performed after the previous tests - if (!kind.isInstance(spans[i])) continue; - - if (count == 0) { - // Safe conversion thanks to the isInstance test above - ret1 = (T) spans[i]; - count++; - } else { - if (count == 1) { - // Safe conversion, but requires a suppressWarning - ret = (T[]) Array.newInstance(kind, spanCount - i + 1); - ret[0] = ret1; - } - - int prio = flags[i] & SPAN_PRIORITY; + if (spanMax >= queryStart) { + count = getSpansRec(queryStart, queryEnd, kind, left, ret, count); + } + } + if (i >= mSpanCount) return count; + int spanStart = mSpanStarts[i]; + if (spanStart > mGapStart) { + spanStart -= mGapLength; + } + if (spanStart <= queryEnd) { + int spanEnd = mSpanEnds[i]; + if (spanEnd > mGapStart) { + spanEnd -= mGapLength; + } + if (spanEnd >= queryStart && + (spanStart == spanEnd || queryStart == queryEnd || + (spanStart != queryEnd && spanEnd != queryStart)) && + kind.isInstance(mSpans[i])) { + int prio = mSpanFlags[i] & SPAN_PRIORITY; if (prio != 0) { int j; @@ -836,32 +906,18 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable System.arraycopy(ret, j, ret, j + 1, count - j); // Safe conversion thanks to the isInstance test above - ret[j] = (T) spans[i]; - count++; + ret[j] = (T) mSpans[i]; } else { // Safe conversion thanks to the isInstance test above - ret[count++] = (T) spans[i]; + ret[count] = (T) mSpans[i]; } + count++; + } + if (count < ret.length && (i & 1) != 0) { + count = getSpansRec(queryStart, queryEnd, kind, rightChild(i), ret, count); } } - - if (count == 0) { - return ArrayUtils.emptyArray(kind); - } - if (count == 1) { - // Safe conversion, but requires a suppressWarning - ret = (T[]) Array.newInstance(kind, 1); - ret[0] = ret1; - return ret; - } - if (count == ret.length) { - return ret; - } - - // Safe conversion, but requires a suppressWarning - T[] nret = (T[]) Array.newInstance(kind, count); - System.arraycopy(ret, 0, nret, 0, count); - return nret; + return count; } /** @@ -870,30 +926,31 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable * begins or ends. */ public int nextSpanTransition(int start, int limit, Class kind) { - int count = mSpanCount; - Object[] spans = mSpans; - int[] starts = mSpanStarts; - int[] ends = mSpanEnds; - int gapstart = mGapStart; - int gaplen = mGapLength; - + if (mSpanCount == 0) return limit; if (kind == null) { kind = Object.class; } + return nextSpanTransitionRec(start, limit, kind, treeRoot()); + } - for (int i = 0; i < count; i++) { - int st = starts[i]; - int en = ends[i]; - - if (st > gapstart) - st -= gaplen; - if (en > gapstart) - en -= gaplen; - - if (st > start && st < limit && kind.isInstance(spans[i])) + private int nextSpanTransitionRec(int start, int limit, Class kind, int i) { + if ((i & 1) != 0) { + // internal tree node + int left = leftChild(i); + if (resolveGap(mSpanMax[left]) > start) { + limit = nextSpanTransitionRec(start, limit, kind, left); + } + } + if (i < mSpanCount) { + int st = resolveGap(mSpanStarts[i]); + int en = resolveGap(mSpanEnds[i]); + if (st > start && st < limit && kind.isInstance(mSpans[i])) limit = st; - if (en > start && en < limit && kind.isInstance(spans[i])) + if (en > start && en < limit && kind.isInstance(mSpans[i])) limit = en; + if (st < limit && (i & 1) != 0) { + limit = nextSpanTransitionRec(start, limit, kind, rightChild(i)); + } } return limit; @@ -1339,6 +1396,118 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable return hash; } + // Primitives for treating span list as binary tree + + // The spans (along with start and end offsets and flags) are stored in linear arrays sorted + // by start offset. For fast searching, there is a binary search structure imposed over these + // arrays. This structure is inorder traversal of a perfect binary tree, a slightly unusual + // but advantageous approach. + + // The value-containing nodes are indexed 0 <= i < n (where n = mSpanCount), thus preserving + // logic that accesses the values as a contiguous array. Other balanced binary tree approaches + // (such as a complete binary tree) would require some shuffling of node indices. + + // Basic properties of this structure: For a perfect binary tree of height m: + // The tree has 2^(m+1) - 1 total nodes. + // The root of the tree has index 2^m - 1. + // All leaf nodes have even index, all interior nodes odd. + // The height of a node of index i is the number of trailing ones in i's binary representation. + // The left child of a node i of height h is i - 2^(h - 1). + // The right child of a node i of height h is i + 2^(h - 1). + + // Note that for arbitrary n, interior nodes of this tree may be >= n. Thus, the general + // structure of a recursive traversal of node i is: + // * traverse left child if i is an interior node + // * process i if i < n + // * traverse right child if i is an interior node and i < n + + private int treeRoot() { + return Integer.highestOneBit(mSpanCount) - 1; + } + + // (i+1) & ~i is equal to 2^(the number of trailing ones in i) + private static int leftChild(int i) { + return i - (((i + 1) & ~i) >> 1); + } + + private static int rightChild(int i) { + return i + (((i + 1) & ~i) >> 1); + } + + // The span arrays are also augmented by an mSpanMax[] array that represents an interval tree + // over the binary tree structure described above. For each node, the mSpanMax[] array contains + // the maximum value of mSpanEnds of that node and its descendants. Thus, traversals can + // easily reject subtrees that contain no spans overlapping the area of interest. + + // Note that mSpanMax[] also has a valid valuefor interior nodes of index >= n, but which have + // descendants of index < n. In these cases, it simply represents the maximum span end of its + // descendants. This is a consequence of the perfect binary tree structure. + private int calcMax(int i) { + int max = 0; + if ((i & 1) != 0) { + // internal tree node + max = calcMax(leftChild(i)); + } + if (i < mSpanCount) { + max = Math.max(max, mSpanEnds[i]); + if ((i & 1) != 0) { + max = Math.max(max, calcMax(rightChild(i))); + } + } + mSpanMax[i] = max; + return max; + } + + // restores binary interval tree invariants after any mutation of span structure + private void restoreInvariants() { + if (mSpanCount == 0) return; + + // invariant 1: span starts are nondecreasing + + // This is a simple insertion sort because we expect it to be mostly sorted. + for (int i = 1; i < mSpanCount; i++) { + if (mSpanStarts[i] < mSpanStarts[i - 1]) { + Object span = mSpans[i]; + int start = mSpanStarts[i]; + int end = mSpanEnds[i]; + int flags = mSpanFlags[i]; + int j = i; + do { + mSpans[j] = mSpans[j - 1]; + mSpanStarts[j] = mSpanStarts[j - 1]; + mSpanEnds[j] = mSpanEnds[j - 1]; + mSpanFlags[j] = mSpanFlags[j - 1]; + j--; + } while (j > 0 && start < mSpanStarts[j - 1]); + mSpans[j] = span; + mSpanStarts[j] = start; + mSpanEnds[j] = end; + mSpanFlags[j] = flags; + invalidateIndex(j); + } + } + + // invariant 2: max is max span end for each node and its descendants + calcMax(treeRoot()); + + // invariant 3: mIndexOfSpan maps spans back to indices + if (mIndexOfSpan == null) { + mIndexOfSpan = new IdentityHashMap<Object, Integer>(); + } + for (int i = mLowWaterMark; i < mSpanCount; i++) { + Integer existing = mIndexOfSpan.get(mSpans[i]); + if (existing == null || existing != i) { + mIndexOfSpan.put(mSpans[i], i); + } + } + mLowWaterMark = Integer.MAX_VALUE; + } + + // Call this on any update to mSpans[], so that mIndexOfSpan can be updated + private void invalidateIndex(int i) { + mLowWaterMark = Math.min(i, mLowWaterMark); + } + private static final InputFilter[] NO_FILTERS = new InputFilter[0]; private InputFilter[] mFilters = NO_FILTERS; @@ -1349,9 +1518,11 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable private Object[] mSpans; private int[] mSpanStarts; private int[] mSpanEnds; + private int[] mSpanMax; // see calcMax() for an explanation of what this array stores private int[] mSpanFlags; private int mSpanCount; - private int mSpanCountBeforeAdd; + private IdentityHashMap<Object, Integer> mIndexOfSpan; + private int mLowWaterMark; // indices below this have not been touched // TODO These value are tightly related to the public SPAN_MARK/POINT values in {@link Spanned} private static final int MARK = 1; @@ -1363,6 +1534,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable private static final int START_SHIFT = 4; // These bits are not (currently) used by SPANNED flags + private static final int SPAN_ADDED = 0x800; private static final int SPAN_START_AT_START = 0x1000; private static final int SPAN_START_AT_END = 0x2000; private static final int SPAN_END_AT_START = 0x4000; diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java index d0ed871..ac98e8a 100644 --- a/core/java/android/text/format/DateUtils.java +++ b/core/java/android/text/format/DateUtils.java @@ -28,9 +28,11 @@ import java.util.Date; import java.util.Formatter; import java.util.GregorianCalendar; import java.util.Locale; +import java.util.TimeZone; import libcore.icu.DateIntervalFormat; import libcore.icu.LocaleData; +import libcore.icu.RelativeDateTimeFormatter; /** * This class contains various date-related utilities for creating text for things like @@ -242,6 +244,8 @@ public class DateUtils /** * Returns a string describing the elapsed time since startTime. + * <p> + * The minimum timespan to report is set to {@link #MINUTE_IN_MILLIS}. * @param startTime some time in the past. * @return a String object containing the elapsed time. * @see #getRelativeTimeSpanString(long, long, long) @@ -289,69 +293,8 @@ public class DateUtils */ public static CharSequence getRelativeTimeSpanString(long time, long now, long minResolution, int flags) { - Resources r = Resources.getSystem(); - boolean abbrevRelative = (flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0; - - boolean past = (now >= time); - long duration = Math.abs(now - time); - - int resId; - long count; - if (duration < MINUTE_IN_MILLIS && minResolution < MINUTE_IN_MILLIS) { - count = duration / SECOND_IN_MILLIS; - if (past) { - if (abbrevRelative) { - resId = com.android.internal.R.plurals.abbrev_num_seconds_ago; - } else { - resId = com.android.internal.R.plurals.num_seconds_ago; - } - } else { - if (abbrevRelative) { - resId = com.android.internal.R.plurals.abbrev_in_num_seconds; - } else { - resId = com.android.internal.R.plurals.in_num_seconds; - } - } - } else if (duration < HOUR_IN_MILLIS && minResolution < HOUR_IN_MILLIS) { - count = duration / MINUTE_IN_MILLIS; - if (past) { - if (abbrevRelative) { - resId = com.android.internal.R.plurals.abbrev_num_minutes_ago; - } else { - resId = com.android.internal.R.plurals.num_minutes_ago; - } - } else { - if (abbrevRelative) { - resId = com.android.internal.R.plurals.abbrev_in_num_minutes; - } else { - resId = com.android.internal.R.plurals.in_num_minutes; - } - } - } else if (duration < DAY_IN_MILLIS && minResolution < DAY_IN_MILLIS) { - count = duration / HOUR_IN_MILLIS; - if (past) { - if (abbrevRelative) { - resId = com.android.internal.R.plurals.abbrev_num_hours_ago; - } else { - resId = com.android.internal.R.plurals.num_hours_ago; - } - } else { - if (abbrevRelative) { - resId = com.android.internal.R.plurals.abbrev_in_num_hours; - } else { - resId = com.android.internal.R.plurals.in_num_hours; - } - } - } else if (duration < WEEK_IN_MILLIS && minResolution < WEEK_IN_MILLIS) { - return getRelativeDayString(r, time, now); - } else { - // We know that we won't be showing the time, so it is safe to pass - // in a null context. - return formatDateRange(null, time, time, flags); - } - - String format = r.getQuantityString(resId, (int) count); - return String.format(format, count); + return RelativeDateTimeFormatter.getRelativeTimeSpanString(Locale.getDefault(), + TimeZone.getDefault(), time, now, minResolution, flags); } /** @@ -360,8 +303,8 @@ public class DateUtils * <p> * Example output strings for the US date format. * <ul> - * <li>3 mins ago, 10:15 AM</li> - * <li>yesterday, 12:20 PM</li> + * <li>3 min. ago, 10:15 AM</li> + * <li>Yesterday, 12:20 PM</li> * <li>Dec 12, 4:12 AM</li> * <li>11/14/2007, 8:20 AM</li> * </ul> @@ -374,86 +317,19 @@ public class DateUtils * @param transitionResolution the elapsed time (in milliseconds) at which * to stop reporting relative measurements. Elapsed times greater * than this resolution will default to normal date formatting. - * For example, will transition from "6 days ago" to "Dec 12" + * For example, will transition from "7 days ago" to "Dec 12" * when using {@link #WEEK_IN_MILLIS}. */ public static CharSequence getRelativeDateTimeString(Context c, long time, long minResolution, long transitionResolution, int flags) { - Resources r = Resources.getSystem(); - - long now = System.currentTimeMillis(); - long duration = Math.abs(now - time); - - // getRelativeTimeSpanString() doesn't correctly format relative dates - // above a week or exact dates below a day, so clamp - // transitionResolution as needed. - if (transitionResolution > WEEK_IN_MILLIS) { - transitionResolution = WEEK_IN_MILLIS; - } else if (transitionResolution < DAY_IN_MILLIS) { - transitionResolution = DAY_IN_MILLIS; - } - - CharSequence timeClause = formatDateRange(c, time, time, FORMAT_SHOW_TIME); - - String result; - if (duration < transitionResolution) { - CharSequence relativeClause = getRelativeTimeSpanString(time, now, minResolution, flags); - result = r.getString(com.android.internal.R.string.relative_time, relativeClause, timeClause); - } else { - CharSequence dateClause = getRelativeTimeSpanString(c, time, false); - result = r.getString(com.android.internal.R.string.date_time, dateClause, timeClause); - } - - return result; - } - - /** - * Returns a string describing a day relative to the current day. For example if the day is - * today this function returns "Today", if the day was a week ago it returns "7 days ago", and - * if the day is in 2 weeks it returns "in 14 days". - * - * @param r the resources - * @param day the relative day to describe in UTC milliseconds - * @param today the current time in UTC milliseconds - */ - private static final String getRelativeDayString(Resources r, long day, long today) { - Locale locale = r.getConfiguration().locale; - if (locale == null) { - locale = Locale.getDefault(); - } - - // TODO: use TimeZone.getOffset instead. - Time startTime = new Time(); - startTime.set(day); - int startDay = Time.getJulianDay(day, startTime.gmtoff); - - Time currentTime = new Time(); - currentTime.set(today); - int currentDay = Time.getJulianDay(today, currentTime.gmtoff); - - int days = Math.abs(currentDay - startDay); - boolean past = (today > day); - - // TODO: some locales name other days too, such as de_DE's "Vorgestern" (today - 2). - if (days == 1) { - if (past) { - return LocaleData.get(locale).yesterday; - } else { - return LocaleData.get(locale).tomorrow; - } - } else if (days == 0) { - return LocaleData.get(locale).today; - } - - int resId; - if (past) { - resId = com.android.internal.R.plurals.num_days_ago; - } else { - resId = com.android.internal.R.plurals.in_num_days; + // Same reason as in formatDateRange() to explicitly indicate 12- or 24-hour format. + if ((flags & (FORMAT_SHOW_TIME | FORMAT_12HOUR | FORMAT_24HOUR)) == FORMAT_SHOW_TIME) { + flags |= DateFormat.is24HourFormat(c) ? FORMAT_24HOUR : FORMAT_12HOUR; } - String format = r.getQuantityString(resId, days); - return String.format(format, days); + return RelativeDateTimeFormatter.getRelativeDateTimeString(Locale.getDefault(), + TimeZone.getDefault(), time, System.currentTimeMillis(), minResolution, + transitionResolution, flags); } private static void initFormatStrings() { diff --git a/core/java/android/transition/ArcMotion.java b/core/java/android/transition/ArcMotion.java index f95fb49..70dfe7f 100644 --- a/core/java/android/transition/ArcMotion.java +++ b/core/java/android/transition/ArcMotion.java @@ -21,7 +21,6 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Path; import android.util.AttributeSet; -import android.util.FloatMath; /** * A PathMotion that generates a curved path along an arc on an imaginary circle containing @@ -257,7 +256,7 @@ public class ArcMotion extends PathMotion { } if (newArcDistance2 != 0) { float ratio2 = newArcDistance2 / arcDist2; - float ratio = FloatMath.sqrt(ratio2); + float ratio = (float) Math.sqrt(ratio2); ex = dx + (ratio * (ex - dx)); ey = dy + (ratio * (ey - dy)); } diff --git a/core/java/android/transition/CircularPropagation.java b/core/java/android/transition/CircularPropagation.java index 51beb51..1e44cfa 100644 --- a/core/java/android/transition/CircularPropagation.java +++ b/core/java/android/transition/CircularPropagation.java @@ -16,7 +16,6 @@ package android.transition; import android.graphics.Rect; -import android.util.FloatMath; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -87,9 +86,9 @@ public class CircularPropagation extends VisibilityPropagation { epicenterY = Math.round(loc[1] + (sceneRoot.getHeight() / 2) + sceneRoot.getTranslationY()); } - float distance = distance(viewCenterX, viewCenterY, epicenterX, epicenterY); - float maxDistance = distance(0, 0, sceneRoot.getWidth(), sceneRoot.getHeight()); - float distanceFraction = distance/maxDistance; + double distance = distance(viewCenterX, viewCenterY, epicenterX, epicenterY); + double maxDistance = distance(0, 0, sceneRoot.getWidth(), sceneRoot.getHeight()); + double distanceFraction = distance/maxDistance; long duration = transition.getDuration(); if (duration < 0) { @@ -99,9 +98,9 @@ public class CircularPropagation extends VisibilityPropagation { return Math.round(duration * directionMultiplier / mPropagationSpeed * distanceFraction); } - private static float distance(float x1, float y1, float x2, float y2) { - float x = x2 - x1; - float y = y2 - y1; - return FloatMath.sqrt((x * x) + (y * y)); + private static double distance(float x1, float y1, float x2, float y2) { + double x = x2 - x1; + double y = y2 - y1; + return Math.hypot(x, y); } } diff --git a/core/java/android/transition/Explode.java b/core/java/android/transition/Explode.java index 0ccdf15..788676a 100644 --- a/core/java/android/transition/Explode.java +++ b/core/java/android/transition/Explode.java @@ -22,7 +22,6 @@ import android.animation.TimeInterpolator; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; -import android.util.FloatMath; import android.view.View; import android.view.ViewGroup; import android.view.animation.AccelerateInterpolator; @@ -143,32 +142,29 @@ public class Explode extends Visibility { int centerX = bounds.centerX(); int centerY = bounds.centerY(); - float xVector = centerX - focalX; - float yVector = centerY - focalY; + double xVector = centerX - focalX; + double yVector = centerY - focalY; if (xVector == 0 && yVector == 0) { // Random direction when View is centered on focal View. - xVector = (float) (Math.random() * 2) - 1; - yVector = (float) (Math.random() * 2) - 1; + xVector = (Math.random() * 2) - 1; + yVector = (Math.random() * 2) - 1; } - float vectorSize = calculateDistance(xVector, yVector); + double vectorSize = Math.hypot(xVector, yVector); xVector /= vectorSize; yVector /= vectorSize; - float maxDistance = + double maxDistance = calculateMaxDistance(sceneRoot, focalX - sceneRootX, focalY - sceneRootY); - outVector[0] = Math.round(maxDistance * xVector); - outVector[1] = Math.round(maxDistance * yVector); + outVector[0] = (int) Math.round(maxDistance * xVector); + outVector[1] = (int) Math.round(maxDistance * yVector); } - private static float calculateMaxDistance(View sceneRoot, int focalX, int focalY) { + private static double calculateMaxDistance(View sceneRoot, int focalX, int focalY) { int maxX = Math.max(focalX, sceneRoot.getWidth() - focalX); int maxY = Math.max(focalY, sceneRoot.getHeight() - focalY); - return calculateDistance(maxX, maxY); + return Math.hypot(maxX, maxY); } - private static float calculateDistance(float x, float y) { - return FloatMath.sqrt((x * x) + (y * y)); - } } diff --git a/core/java/android/transition/PatternPathMotion.java b/core/java/android/transition/PatternPathMotion.java index a609df6..773c387 100644 --- a/core/java/android/transition/PatternPathMotion.java +++ b/core/java/android/transition/PatternPathMotion.java @@ -23,7 +23,6 @@ import android.graphics.Matrix; import android.graphics.Path; import android.graphics.PathMeasure; import android.util.AttributeSet; -import android.util.FloatMath; import android.util.PathParser; /** @@ -119,7 +118,7 @@ public class PatternPathMotion extends PathMotion { mTempMatrix.setTranslate(-startX, -startY); float dx = endX - startX; float dy = endY - startY; - float distance = distance(dx, dy); + float distance = (float) Math.hypot(dx, dy); float scale = 1 / distance; mTempMatrix.postScale(scale, scale); double angle = Math.atan2(dy, dx); @@ -130,9 +129,9 @@ public class PatternPathMotion extends PathMotion { @Override public Path getPath(float startX, float startY, float endX, float endY) { - float dx = endX - startX; - float dy = endY - startY; - float length = distance(dx, dy); + double dx = endX - startX; + double dy = endY - startY; + float length = (float) Math.hypot(dx, dy); double angle = Math.atan2(dy, dx); mTempMatrix.setScale(length, length); @@ -143,7 +142,4 @@ public class PatternPathMotion extends PathMotion { return path; } - private static float distance(float x, float y) { - return FloatMath.sqrt((x * x) + (y * y)); - } } diff --git a/core/java/android/transition/SidePropagation.java b/core/java/android/transition/SidePropagation.java index ad6c2dd..5dd1fff 100644 --- a/core/java/android/transition/SidePropagation.java +++ b/core/java/android/transition/SidePropagation.java @@ -16,7 +16,6 @@ package android.transition; import android.graphics.Rect; -import android.util.FloatMath; import android.util.Log; import android.view.Gravity; import android.view.View; diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java index 6925756..391347e 100644 --- a/core/java/android/widget/EdgeEffect.java +++ b/core/java/android/widget/EdgeEffect.java @@ -24,7 +24,6 @@ import android.graphics.Rect; import android.content.Context; import android.graphics.Canvas; -import android.util.FloatMath; import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; @@ -220,8 +219,8 @@ public class EdgeEffect { if (mPullDistance == 0) { mGlowScaleY = mGlowScaleYStart = 0; } else { - final float scale = Math.max(0, 1 - 1 / - FloatMath.sqrt(Math.abs(mPullDistance) * mBounds.height()) - 0.3f) / 0.7f; + final float scale = (float) (Math.max(0, 1 - 1 / + Math.sqrt(Math.abs(mPullDistance) * mBounds.height()) - 0.3d) / 0.7d); mGlowScaleY = mGlowScaleYStart = scale; } |