summaryrefslogtreecommitdiffstats
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/app/UiModeManager.java7
-rw-r--r--core/java/android/app/admin/DeviceAdminReceiver.java13
-rw-r--r--core/java/android/content/Context.java15
-rw-r--r--core/java/android/hardware/Sensor.java92
-rw-r--r--core/java/android/net/ConnectivityManager.java4
-rw-r--r--core/java/android/provider/Settings.java2
-rw-r--r--core/java/android/text/SpannableStringBuilder.java550
-rw-r--r--core/java/android/text/format/DateUtils.java154
-rw-r--r--core/java/android/transition/ArcMotion.java3
-rw-r--r--core/java/android/transition/CircularPropagation.java15
-rw-r--r--core/java/android/transition/Explode.java24
-rw-r--r--core/java/android/transition/PatternPathMotion.java12
-rw-r--r--core/java/android/transition/SidePropagation.java1
-rw-r--r--core/java/android/widget/EdgeEffect.java5
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;
}