Merge "Fix issue #22023824: Download folder is not created in internal storage" into mnc-dev
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 75289f7..33cc962 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1062,6 +1062,15 @@
* capturing a high-resolution JPEG image will automatically trigger a
* precapture sequence before the high-resolution capture, including
* potentially firing a pre-capture flash.</p>
+ * <p>Using the precapture trigger and the auto-focus trigger {@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}
+ * simultaneously is allowed. However, since these triggers often require cooperation between
+ * the auto-focus and auto-exposure routines (for example, the may need to be enabled for a
+ * focus sweep), the camera device may delay acting on a later trigger until the previous
+ * trigger has been fully handled. This may lead to longer intervals between the trigger and
+ * changes to {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} indicating the start of the precapture sequence, for
+ * example.</p>
+ * <p>If both the precapture and the auto-focus trigger are activated on the same request, then
+ * the camera device will complete them in the optimal order for that device.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE IDLE}</li>
@@ -1075,6 +1084,7 @@
*
* @see CaptureRequest#CONTROL_AE_LOCK
* @see CaptureResult#CONTROL_AE_STATE
+ * @see CaptureRequest#CONTROL_AF_TRIGGER
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE
@@ -1179,6 +1189,12 @@
* START for multiple captures in a row means restarting the AF operation over
* and over again.</p>
* <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what the trigger means for each AF mode.</p>
+ * <p>Using the autofocus trigger and the precapture trigger {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}
+ * simultaneously is allowed. However, since these triggers often require cooperation between
+ * the auto-focus and auto-exposure routines (for example, the may need to be enabled for a
+ * focus sweep), the camera device may delay acting on a later trigger until the previous
+ * trigger has been fully handled. This may lead to longer intervals between the trigger and
+ * changes to {@link CaptureResult#CONTROL_AF_STATE android.control.afState}, for example.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #CONTROL_AF_TRIGGER_IDLE IDLE}</li>
@@ -1187,6 +1203,7 @@
* </ul></p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
* @see CaptureResult#CONTROL_AF_STATE
* @see #CONTROL_AF_TRIGGER_IDLE
* @see #CONTROL_AF_TRIGGER_START
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 1d31109..9dee045 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -779,6 +779,15 @@
* capturing a high-resolution JPEG image will automatically trigger a
* precapture sequence before the high-resolution capture, including
* potentially firing a pre-capture flash.</p>
+ * <p>Using the precapture trigger and the auto-focus trigger {@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}
+ * simultaneously is allowed. However, since these triggers often require cooperation between
+ * the auto-focus and auto-exposure routines (for example, the may need to be enabled for a
+ * focus sweep), the camera device may delay acting on a later trigger until the previous
+ * trigger has been fully handled. This may lead to longer intervals between the trigger and
+ * changes to {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} indicating the start of the precapture sequence, for
+ * example.</p>
+ * <p>If both the precapture and the auto-focus trigger are activated on the same request, then
+ * the camera device will complete them in the optimal order for that device.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE IDLE}</li>
@@ -792,6 +801,7 @@
*
* @see CaptureRequest#CONTROL_AE_LOCK
* @see CaptureResult#CONTROL_AE_STATE
+ * @see CaptureRequest#CONTROL_AF_TRIGGER
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE
@@ -1139,6 +1149,12 @@
* START for multiple captures in a row means restarting the AF operation over
* and over again.</p>
* <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what the trigger means for each AF mode.</p>
+ * <p>Using the autofocus trigger and the precapture trigger {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}
+ * simultaneously is allowed. However, since these triggers often require cooperation between
+ * the auto-focus and auto-exposure routines (for example, the may need to be enabled for a
+ * focus sweep), the camera device may delay acting on a later trigger until the previous
+ * trigger has been fully handled. This may lead to longer intervals between the trigger and
+ * changes to {@link CaptureResult#CONTROL_AF_STATE android.control.afState}, for example.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #CONTROL_AF_TRIGGER_IDLE IDLE}</li>
@@ -1147,6 +1163,7 @@
* </ul></p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
* @see CaptureResult#CONTROL_AF_STATE
* @see #CONTROL_AF_TRIGGER_IDLE
* @see #CONTROL_AF_TRIGGER_START
diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java
index 5dda8a7..3688cfa 100644
--- a/core/java/android/text/method/WordIterator.java
+++ b/core/java/android/text/method/WordIterator.java
@@ -147,24 +147,13 @@
* @throws IllegalArgumentException is offset is not valid.
*/
public int getBeginning(int offset) {
- final int shiftedOffset = offset - mOffsetShift;
- checkOffsetIsValid(shiftedOffset);
-
- if (isOnLetterOrDigit(shiftedOffset)) {
- if (mIterator.isBoundary(shiftedOffset)) {
- return shiftedOffset + mOffsetShift;
- } else {
- return mIterator.preceding(shiftedOffset) + mOffsetShift;
- }
- } else {
- if (isAfterLetterOrDigit(shiftedOffset)) {
- return mIterator.preceding(shiftedOffset) + mOffsetShift;
- }
- }
- return BreakIterator.DONE;
+ // TODO: Check if usage of this can be updated to getBeginning(offset, true) if
+ // so this method can be removed.
+ return getBeginning(offset, false);
}
- /** If <code>offset</code> is within a word, returns the index of the last character of that
+ /**
+ * If <code>offset</code> is within a word, returns the index of the last character of that
* word plus one, otherwise returns BreakIterator.DONE.
*
* The offsets that are considered to be part of a word are the indexes of its characters,
@@ -177,11 +166,106 @@
* @throws IllegalArgumentException is offset is not valid.
*/
public int getEnd(int offset) {
+ // TODO: Check if usage of this can be updated to getEnd(offset, true), if
+ // so this method can be removed.
+ return getEnd(offset, false);
+ }
+
+ /**
+ * If the <code>offset</code> is within a word or on a word boundary that can only be
+ * considered the start of a word (e.g. _word where "_" is any character that would not
+ * be considered part of the word) then this returns the index of the first character of
+ * that word.
+ *
+ * If the offset is on a word boundary that can be considered the start and end of a
+ * word, e.g. AABB (where AA and BB are both words) and the offset is the boundary
+ * between AA and BB, this would return the start of the previous word, AA.
+ *
+ * Returns BreakIterator.DONE if there is no previous boundary.
+ *
+ * @throws IllegalArgumentException is offset is not valid.
+ */
+ public int getPrevWordBeginningOnTwoWordsBoundary(int offset) {
+ return getBeginning(offset, true);
+ }
+
+ /**
+ * If the <code>offset</code> is within a word or on a word boundary that can only be
+ * considered the end of a word (e.g. word_ where "_" is any character that would not
+ * be considered part of the word) then this returns the index of the last character
+ * plus one of that word.
+ *
+ * If the offset is on a word boundary that can be considered the start and end of a
+ * word, e.g. AABB (where AA and BB are both words) and the offset is the boundary
+ * between AA and BB, this would return the end of the next word, BB.
+ *
+ * Returns BreakIterator.DONE if there is no next boundary.
+ *
+ * @throws IllegalArgumentException is offset is not valid.
+ */
+ public int getNextWordEndOnTwoWordBoundary(int offset) {
+ return getEnd(offset, true);
+ }
+
+ /**
+ * If the <code>offset</code> is within a word or on a word boundary that can only be
+ * considered the start of a word (e.g. _word where "_" is any character that would not
+ * be considered part of the word) then this returns the index of the first character of
+ * that word.
+ *
+ * If the offset is on a word boundary that can be considered the start and end of a
+ * word, e.g. AABB (where AA and BB are both words) and the offset is the boundary
+ * between AA and BB, and getPrevWordBeginningOnTwoWordsBoundary is true then this would
+ * return the start of the previous word, AA. Otherwise it would return the current offset,
+ * the start of BB.
+ *
+ * Returns BreakIterator.DONE if there is no previous boundary.
+ *
+ * @throws IllegalArgumentException is offset is not valid.
+ */
+ private int getBeginning(int offset, boolean getPrevWordBeginningOnTwoWordsBoundary) {
+ final int shiftedOffset = offset - mOffsetShift;
+ checkOffsetIsValid(shiftedOffset);
+
+ if (isOnLetterOrDigit(shiftedOffset)) {
+ if (mIterator.isBoundary(shiftedOffset)
+ && (!isAfterLetterOrDigit(shiftedOffset)
+ || !getPrevWordBeginningOnTwoWordsBoundary)) {
+ return shiftedOffset + mOffsetShift;
+ } else {
+ return mIterator.preceding(shiftedOffset) + mOffsetShift;
+ }
+ } else {
+ if (isAfterLetterOrDigit(shiftedOffset)) {
+ return mIterator.preceding(shiftedOffset) + mOffsetShift;
+ }
+ }
+ return BreakIterator.DONE;
+ }
+
+ /**
+ * If the <code>offset</code> is within a word or on a word boundary that can only be
+ * considered the end of a word (e.g. word_ where "_" is any character that would not be
+ * considered part of the word) then this returns the index of the last character plus one
+ * of that word.
+ *
+ * If the offset is on a word boundary that can be considered the start and end of a
+ * word, e.g. AABB (where AA and BB are both words) and the offset is the boundary
+ * between AA and BB, and getNextWordEndOnTwoWordBoundary is true then this would return
+ * the end of the next word, BB. Otherwise it would return the current offset, the end
+ * of AA.
+ *
+ * Returns BreakIterator.DONE if there is no next boundary.
+ *
+ * @throws IllegalArgumentException is offset is not valid.
+ */
+ private int getEnd(int offset, boolean getNextWordEndOnTwoWordBoundary) {
final int shiftedOffset = offset - mOffsetShift;
checkOffsetIsValid(shiftedOffset);
if (isAfterLetterOrDigit(shiftedOffset)) {
- if (mIterator.isBoundary(shiftedOffset)) {
+ if (mIterator.isBoundary(shiftedOffset)
+ && (!isOnLetterOrDigit(shiftedOffset) || !getNextWordEndOnTwoWordBoundary)) {
return shiftedOffset + mOffsetShift;
} else {
return mIterator.following(shiftedOffset) + mOffsetShift;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 48e69a1..c96d39b 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -123,6 +123,7 @@
private static final float[] TEMP_POSITION = new float[2];
private static int DRAG_SHADOW_MAX_TEXT_LENGTH = 20;
private static final float LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS = 0.5f;
+ private static final int UNSET_X_VALUE = -1;
// Tag used when the Editor maintains its own separate UndoManager.
private static final String UNDO_OWNER_TAG = "Editor";
@@ -735,7 +736,7 @@
retOffset = getWordIteratorWithText().getPunctuationBeginning(offset);
} else {
// Not on a punctuation boundary, find the word start.
- retOffset = getWordIteratorWithText().getBeginning(offset);
+ retOffset = getWordIteratorWithText().getPrevWordBeginningOnTwoWordsBoundary(offset);
}
if (retOffset == BreakIterator.DONE) {
return offset;
@@ -750,7 +751,7 @@
retOffset = getWordIteratorWithText().getPunctuationEnd(offset);
} else {
// Not on a punctuation boundary, find the word end.
- retOffset = getWordIteratorWithText().getEnd(offset);
+ retOffset = getWordIteratorWithText().getNextWordEndOnTwoWordBoundary(offset);
}
if (retOffset == BreakIterator.DONE) {
return offset;
@@ -4067,6 +4068,10 @@
private boolean mInWord = false;
// Difference between touch position and word boundary position.
private float mTouchWordDelta;
+ // X value of the previous updatePosition call.
+ private float mPrevX;
+ // Indicates if the handle has moved a boundary between LTR and RTL text.
+ private boolean mLanguageDirectionChanged = false;
public SelectionStartHandleView(Drawable drawableLtr, Drawable drawableRtl) {
super(drawableLtr, drawableRtl);
@@ -4127,12 +4132,48 @@
int end = getWordEnd(offset);
int start = getWordStart(offset);
- if (offset < mPreviousOffset) {
+ if (mPrevX == UNSET_X_VALUE) {
+ mPrevX = x;
+ }
+
+ final int selectionStart = mTextView.getSelectionStart();
+ final boolean selectionStartRtl = layout.isRtlCharAt(selectionStart);
+ final boolean atRtl = layout.isRtlCharAt(offset);
+ final boolean isLvlBoundary = layout.isLevelBoundary(offset);
+ boolean isExpanding;
+
+ // We can't determine if the user is expanding or shrinking the selection if they're
+ // on a bi-di boundary, so until they've moved past the boundary we'll just place
+ // the cursor at the current position.
+ if (isLvlBoundary || (selectionStartRtl && !atRtl) || (!selectionStartRtl && atRtl)) {
+ // We're on a boundary or this is the first direction change -- just update
+ // to the current position.
+ mLanguageDirectionChanged = true;
+ mTouchWordDelta = 0.0f;
+ positionAtCursorOffset(offset, false);
+ return;
+ } else if (mLanguageDirectionChanged && !isLvlBoundary) {
+ // We've just moved past the boundary so update the position. After this we can
+ // figure out if the user is expanding or shrinking to go by word or character.
+ positionAtCursorOffset(offset, false);
+ mTouchWordDelta = 0.0f;
+ mLanguageDirectionChanged = false;
+ return;
+ } else {
+ final float xDiff = x - mPrevX;
+ if (atRtl) {
+ isExpanding = xDiff > 0 || currLine > mPrevLine;
+ } else {
+ isExpanding = xDiff < 0 || currLine < mPrevLine;
+ }
+ }
+
+ if (isExpanding) {
// User is increasing the selection.
if (!mInWord || currLine < mPrevLine) {
// We're not in a word, or we're on a different line so we'll expand by
// word. First ensure the user has at least entered the next word.
- int offsetToWord = Math.min((end - start) / 2, 2);
+ int offsetToWord = (end - start) / 2;
if (offset <= end - offsetToWord || currLine < mPrevLine) {
offset = start;
} else {
@@ -4182,6 +4223,7 @@
}
positionAtCursorOffset(offset, false);
}
+ mPrevX = x;
}
@Override
@@ -4196,6 +4238,7 @@
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
// Reset the touch word offset when the user has lifted their finger.
mTouchWordDelta = 0.0f;
+ mPrevX = UNSET_X_VALUE;
}
return superResult;
}
@@ -4206,6 +4249,10 @@
private boolean mInWord = false;
// Difference between touch position and word boundary position.
private float mTouchWordDelta;
+ // X value of the previous updatePosition call.
+ private float mPrevX;
+ // Indicates if the handle has moved a boundary between LTR and RTL text.
+ private boolean mLanguageDirectionChanged = false;
public SelectionEndHandleView(Drawable drawableLtr, Drawable drawableRtl) {
super(drawableLtr, drawableRtl);
@@ -4266,12 +4313,48 @@
int end = getWordEnd(offset);
int start = getWordStart(offset);
- if (offset > mPreviousOffset) {
+ if (mPrevX == UNSET_X_VALUE) {
+ mPrevX = x;
+ }
+
+ final int selectionEnd = mTextView.getSelectionEnd();
+ final boolean selectionEndRtl = layout.isRtlCharAt(selectionEnd);
+ final boolean atRtl = layout.isRtlCharAt(offset);
+ final boolean isLvlBoundary = layout.isLevelBoundary(offset);
+ boolean isExpanding;
+
+ // We can't determine if the user is expanding or shrinking the selection if they're
+ // on a bi-di boundary, so until they've moved past the boundary we'll just place
+ // the cursor at the current position.
+ if (isLvlBoundary || (selectionEndRtl && !atRtl) || (!selectionEndRtl && atRtl)) {
+ // We're on a boundary or this is the first direction change -- just update
+ // to the current position.
+ mLanguageDirectionChanged = true;
+ mTouchWordDelta = 0.0f;
+ positionAtCursorOffset(offset, false);
+ return;
+ } else if (mLanguageDirectionChanged && !isLvlBoundary) {
+ // We've just moved past the boundary so update the position. After this we can
+ // figure out if the user is expanding or shrinking to go by word or character.
+ positionAtCursorOffset(offset, false);
+ mTouchWordDelta = 0.0f;
+ mLanguageDirectionChanged = false;
+ return;
+ } else {
+ final float xDiff = x - mPrevX;
+ if (atRtl) {
+ isExpanding = xDiff < 0 || currLine < mPrevLine;
+ } else {
+ isExpanding = xDiff > 0 || currLine > mPrevLine;
+ }
+ }
+
+ if (isExpanding) {
// User is increasing the selection.
if (!mInWord || currLine > mPrevLine) {
// We're not in a word, or we're on a different line so we'll expand by
// word. First ensure the user has at least entered the next word.
- int midPoint = Math.min((end - start) / 2, 2);
+ int midPoint = (end - start) / 2;
if (offset >= start + midPoint || currLine > mPrevLine) {
offset = end;
} else {
@@ -4321,6 +4404,7 @@
}
positionAtCursorOffset(offset, false);
}
+ mPrevX = x;
}
@Override
@@ -4335,6 +4419,7 @@
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
// Reset the touch word offset when the user has lifted their finger.
mTouchWordDelta = 0.0f;
+ mPrevX = UNSET_X_VALUE;
}
return superResult;
}
diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h
index 5e9b213..c98932c 100644
--- a/libs/hwui/Outline.h
+++ b/libs/hwui/Outline.h
@@ -19,6 +19,7 @@
#include <SkPath.h>
#include "Rect.h"
+#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
@@ -85,6 +86,11 @@
return mShouldClip && (mType == kOutlineType_RoundRect);
}
+ bool willRoundRectClip() const {
+ // only round rect outlines can be used for clipping
+ return willClip() && MathUtils::isPositive(mRadius);
+ }
+
bool getAsRoundRect(Rect* outRect, float* outRadius) const {
if (mType == kOutlineType_RoundRect) {
outRect->set(mBounds);
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 81cf2df..11abd70 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -169,7 +169,7 @@
bool functorsNeedLayer = ancestorDictatesFunctorsNeedLayer
// Round rect clipping forces layer for functors
- || CC_UNLIKELY(getOutline().willClip())
+ || CC_UNLIKELY(getOutline().willRoundRectClip())
|| CC_UNLIKELY(getRevealClip().willClip())
// Complex matrices forces layer, due to stencil clipping
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index a806440..b3b2b97 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -619,10 +619,6 @@
com.android.internal.R.bool.config_useVolumeKeySounds);
mUseFixedVolume = getContext().getResources().getBoolean(
com.android.internal.R.bool.config_useFixedVolume);
- sAudioPortEventHandler.init();
-
- mPortListener = new OnAmPortUpdateListener();
- registerAudioPortUpdateListener(mPortListener);
}
private Context getContext() {
@@ -3554,6 +3550,7 @@
* @hide
*/
public void registerAudioPortUpdateListener(OnAudioPortUpdateListener l) {
+ sAudioPortEventHandler.init();
sAudioPortEventHandler.registerListener(l);
}
@@ -3586,6 +3583,7 @@
static int updateAudioPortCache(ArrayList<AudioPort> ports, ArrayList<AudioPatch> patches,
ArrayList<AudioPort> previousPorts) {
+ sAudioPortEventHandler.init();
synchronized (sAudioPortGeneration) {
if (sAudioPortGeneration == AUDIOPORT_GENERATION_INIT) {
@@ -3849,6 +3847,12 @@
android.os.Handler handler) {
if (callback != null && !mDeviceCallbacks.containsKey(callback)) {
synchronized (mDeviceCallbacks) {
+ if (mDeviceCallbacks.size() == 0) {
+ if (mPortListener == null) {
+ mPortListener = new OnAmPortUpdateListener();
+ }
+ registerAudioPortUpdateListener(mPortListener);
+ }
NativeEventHandlerDelegate delegate =
new NativeEventHandlerDelegate(callback, handler);
mDeviceCallbacks.put(callback, delegate);
@@ -3867,6 +3871,9 @@
synchronized (mDeviceCallbacks) {
if (mDeviceCallbacks.containsKey(callback)) {
mDeviceCallbacks.remove(callback);
+ if (mDeviceCallbacks.size() == 0) {
+ unregisterAudioPortUpdateListener(mPortListener);
+ }
}
}
}