Merge "Fixed issue with resized stack sticking to the top of the screen."
diff --git a/api/current.txt b/api/current.txt
index c7dbd49..123d8cc 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -529,6 +529,7 @@
field public static final int ellipsize = 16842923; // 0x10100ab
field public static final int ems = 16843096; // 0x1010158
field public static final int enabled = 16842766; // 0x101000e
+ field public static final int end = 16843997; // 0x10104dd
field public static final int endColor = 16843166; // 0x101019e
field public static final deprecated int endYear = 16843133; // 0x101017d
field public static final int enterFadeDuration = 16843532; // 0x101030c
@@ -1128,6 +1129,7 @@
field public static final int stackFromBottom = 16843005; // 0x10100fd
field public static final int stackViewStyle = 16843838; // 0x101043e
field public static final int starStyle = 16842882; // 0x1010082
+ field public static final int start = 16843996; // 0x10104dc
field public static final int startColor = 16843165; // 0x101019d
field public static final int startDelay = 16843746; // 0x10103e2
field public static final int startOffset = 16843198; // 0x10101be
@@ -11946,6 +11948,9 @@
method public android.graphics.drawable.Drawable findDrawableByLayerId(int);
method public android.graphics.drawable.Drawable getDrawable(int);
method public int getId(int);
+ method public int getLayerGravity(int);
+ method public int getLayerHeight(int);
+ method public int getLayerWidth(int);
method public int getNumberOfLayers();
method public int getOpacity();
method public int getPaddingMode();
@@ -11955,7 +11960,10 @@
method public void setColorFilter(android.graphics.ColorFilter);
method public boolean setDrawableByLayerId(int, android.graphics.drawable.Drawable);
method public void setId(int, int);
+ method public void setLayerGravity(int, int);
method public void setLayerInset(int, int, int, int, int);
+ method public void setLayerInsetRelative(int, int, int, int, int);
+ method public void setLayerSize(int, int, int);
method public void setOpacity(int);
method public void setPaddingMode(int);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
@@ -12696,11 +12704,14 @@
field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> CONTROL_AE_COMPENSATION_RANGE;
field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Rational> CONTROL_AE_COMPENSATION_STEP;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AE_LOCK_AVAILABLE;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AF_AVAILABLE_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_EFFECTS;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_SCENE_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AWB_AVAILABLE_MODES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AWB_LOCK_AVAILABLE;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AE;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AF;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AWB;
diff --git a/api/system-current.txt b/api/system-current.txt
index e1a236f..6224dfb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -601,6 +601,7 @@
field public static final int ellipsize = 16842923; // 0x10100ab
field public static final int ems = 16843096; // 0x1010158
field public static final int enabled = 16842766; // 0x101000e
+ field public static final int end = 16843997; // 0x10104dd
field public static final int endColor = 16843166; // 0x101019e
field public static final deprecated int endYear = 16843133; // 0x101017d
field public static final int enterFadeDuration = 16843532; // 0x101030c
@@ -1204,6 +1205,7 @@
field public static final int stackFromBottom = 16843005; // 0x10100fd
field public static final int stackViewStyle = 16843838; // 0x101043e
field public static final int starStyle = 16842882; // 0x1010082
+ field public static final int start = 16843996; // 0x10104dc
field public static final int startColor = 16843165; // 0x101019d
field public static final int startDelay = 16843746; // 0x10103e2
field public static final int startOffset = 16843198; // 0x10101be
@@ -12214,6 +12216,9 @@
method public android.graphics.drawable.Drawable findDrawableByLayerId(int);
method public android.graphics.drawable.Drawable getDrawable(int);
method public int getId(int);
+ method public int getLayerGravity(int);
+ method public int getLayerHeight(int);
+ method public int getLayerWidth(int);
method public int getNumberOfLayers();
method public int getOpacity();
method public int getPaddingMode();
@@ -12223,7 +12228,10 @@
method public void setColorFilter(android.graphics.ColorFilter);
method public boolean setDrawableByLayerId(int, android.graphics.drawable.Drawable);
method public void setId(int, int);
+ method public void setLayerGravity(int, int);
method public void setLayerInset(int, int, int, int, int);
+ method public void setLayerInsetRelative(int, int, int, int, int);
+ method public void setLayerSize(int, int, int);
method public void setOpacity(int);
method public void setPaddingMode(int);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
@@ -12964,11 +12972,14 @@
field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> CONTROL_AE_COMPENSATION_RANGE;
field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Rational> CONTROL_AE_COMPENSATION_STEP;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AE_LOCK_AVAILABLE;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AF_AVAILABLE_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_EFFECTS;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_SCENE_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AWB_AVAILABLE_MODES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AWB_LOCK_AVAILABLE;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AE;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AF;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AWB;
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 5310071..16bc6a4 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -422,6 +422,17 @@
new Key<Rational>("android.control.aeCompensationStep", Rational.class);
/**
+ * <p>Whether the camera device supports {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock}</p>
+ * <p>LIMITED or FULL devices will always list <code>true</code></p>
+ * <p>This key is available on all devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_AE_LOCK
+ */
+ @PublicKey
+ public static final Key<Boolean> CONTROL_AE_LOCK_AVAILABLE =
+ new Key<Boolean>("android.control.aeLockAvailable", boolean.class);
+
+ /**
* <p>List of auto-focus (AF) modes for {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode} that are
* supported by this camera device.</p>
* <p>Not all the auto-focus modes may be supported by a
@@ -469,6 +480,22 @@
new Key<int[]>("android.control.availableEffects", int[].class);
/**
+ * <p>List of control modes for {@link CaptureRequest#CONTROL_MODE android.control.mode} that are supported by this camera
+ * device.</p>
+ * <p>This list contains control modes that can be set for the camera device.
+ * LEGACY mode devices will always support AUTO mode. LIMITED and FULL
+ * devices will always support OFF, AUTO modes.</p>
+ * <p><b>Range of valid values:</b><br>
+ * Any value listed in {@link CaptureRequest#CONTROL_MODE android.control.mode}</p>
+ * <p>This key is available on all devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_MODE
+ */
+ @PublicKey
+ public static final Key<int[]> CONTROL_AVAILABLE_MODES =
+ new Key<int[]>("android.control.availableModes", int[].class);
+
+ /**
* <p>List of scene modes for {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} that are supported by this camera
* device.</p>
* <p>This list contains scene modes that can be set for the camera device.
@@ -530,6 +557,17 @@
new Key<int[]>("android.control.awbAvailableModes", int[].class);
/**
+ * <p>Whether the camera device supports {@link CaptureRequest#CONTROL_AWB_LOCK android.control.awbLock}</p>
+ * <p>LIMITED or FULL devices will always list <code>true</code></p>
+ * <p>This key is available on all devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_AWB_LOCK
+ */
+ @PublicKey
+ public static final Key<Boolean> CONTROL_AWB_LOCK_AVAILABLE =
+ new Key<Boolean>("android.control.awbLockAvailable", boolean.class);
+
+ /**
* <p>List of the maximum number of regions that can be used for metering in
* auto-exposure (AE), auto-white balance (AWB), and auto-focus (AF);
* this corresponds to the the maximum number of elements in
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index fb37ae5..e1b14cc 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -552,6 +552,8 @@
* in this matrix result metadata. The transform should keep the magnitude
* of the output color values within <code>[0, 1.0]</code> (assuming input color
* values is within the normalized range <code>[0, 1.0]</code>), or clipping may occur.</p>
+ * <p>The valid range of each matrix element varies on different devices, but
+ * values within [-1.5, 3.0] are guaranteed not to be clipped.</p>
* <p><b>Units</b>: Unitless scale factors</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Full capability</b> -
@@ -575,6 +577,10 @@
* TRANSFORM_MATRIX.</p>
* <p>The gains in the result metadata are the gains actually
* applied by the camera device to the current frame.</p>
+ * <p>The valid range of gains varies on different devices, but gains
+ * between [1.0, 3.0] are guaranteed not to be clipped. Even if a given
+ * device allows gains below 1.0, this is usually not recommended because
+ * this can create color artifacts.</p>
* <p><b>Units</b>: Unitless gain factors</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Full capability</b> -
@@ -1239,10 +1245,6 @@
* update, as if this frame is never captured. This mode can be used in the scenario
* where the application doesn't want a 3A manual control capture to affect
* the subsequent auto 3A capture results.</p>
- * <p>LEGACY mode devices will only support AUTO and USE_SCENE_MODE modes.
- * LIMITED mode devices will only support OFF and OFF_KEEP_STATE if they
- * support the MANUAL_SENSOR and MANUAL_POST_PROCSESING capabilities.
- * FULL mode devices will always support OFF and OFF_KEEP_STATE.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #CONTROL_MODE_OFF OFF}</li>
@@ -1250,9 +1252,12 @@
* <li>{@link #CONTROL_MODE_USE_SCENE_MODE USE_SCENE_MODE}</li>
* <li>{@link #CONTROL_MODE_OFF_KEEP_STATE OFF_KEEP_STATE}</li>
* </ul></p>
+ * <p><b>Available values for this device:</b><br>
+ * {@link CameraCharacteristics#CONTROL_AVAILABLE_MODES android.control.availableModes}</p>
* <p>This key is available on all devices.</p>
*
* @see CaptureRequest#CONTROL_AF_MODE
+ * @see CameraCharacteristics#CONTROL_AVAILABLE_MODES
* @see #CONTROL_MODE_OFF
* @see #CONTROL_MODE_AUTO
* @see #CONTROL_MODE_USE_SCENE_MODE
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 5642f6f..5bf5b29 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -403,6 +403,8 @@
* in this matrix result metadata. The transform should keep the magnitude
* of the output color values within <code>[0, 1.0]</code> (assuming input color
* values is within the normalized range <code>[0, 1.0]</code>), or clipping may occur.</p>
+ * <p>The valid range of each matrix element varies on different devices, but
+ * values within [-1.5, 3.0] are guaranteed not to be clipped.</p>
* <p><b>Units</b>: Unitless scale factors</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Full capability</b> -
@@ -426,6 +428,10 @@
* TRANSFORM_MATRIX.</p>
* <p>The gains in the result metadata are the gains actually
* applied by the camera device to the current frame.</p>
+ * <p>The valid range of gains varies on different devices, but gains
+ * between [1.0, 3.0] are guaranteed not to be clipped. Even if a given
+ * device allows gains below 1.0, this is usually not recommended because
+ * this can create color artifacts.</p>
* <p><b>Units</b>: Unitless gain factors</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Full capability</b> -
@@ -1855,10 +1861,6 @@
* update, as if this frame is never captured. This mode can be used in the scenario
* where the application doesn't want a 3A manual control capture to affect
* the subsequent auto 3A capture results.</p>
- * <p>LEGACY mode devices will only support AUTO and USE_SCENE_MODE modes.
- * LIMITED mode devices will only support OFF and OFF_KEEP_STATE if they
- * support the MANUAL_SENSOR and MANUAL_POST_PROCSESING capabilities.
- * FULL mode devices will always support OFF and OFF_KEEP_STATE.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #CONTROL_MODE_OFF OFF}</li>
@@ -1866,9 +1868,12 @@
* <li>{@link #CONTROL_MODE_USE_SCENE_MODE USE_SCENE_MODE}</li>
* <li>{@link #CONTROL_MODE_OFF_KEEP_STATE OFF_KEEP_STATE}</li>
* </ul></p>
+ * <p><b>Available values for this device:</b><br>
+ * {@link CameraCharacteristics#CONTROL_AVAILABLE_MODES android.control.availableModes}</p>
* <p>This key is available on all devices.</p>
*
* @see CaptureRequest#CONTROL_AF_MODE
+ * @see CameraCharacteristics#CONTROL_AVAILABLE_MODES
* @see #CONTROL_MODE_OFF
* @see #CONTROL_MODE_AUTO
* @see #CONTROL_MODE_USE_SCENE_MODE
diff --git a/core/java/android/hardware/camera2/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java
index 6fc99ac..33d539c2 100644
--- a/core/java/android/hardware/camera2/DngCreator.java
+++ b/core/java/android/hardware/camera2/DngCreator.java
@@ -530,9 +530,9 @@
int uPixStride = uPlane.getPixelStride();
byte[] yuvPixel = { 0, 0, 0 };
- byte[] yFullRow = new byte[yPixStride * width];
- byte[] uFullRow = new byte[uPixStride * width / 2];
- byte[] vFullRow = new byte[vPixStride * width / 2];
+ byte[] yFullRow = new byte[yPixStride * (width - 1) + 1];
+ byte[] uFullRow = new byte[uPixStride * (width / 2 - 1) + 1];
+ byte[] vFullRow = new byte[vPixStride * (width / 2 - 1) + 1];
byte[] finalRow = new byte[BYTES_PER_RGB_PIX * width];
for (int i = 0; i < height; i++) {
int halfH = i / 2;
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 347db05..802b938 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -474,6 +474,15 @@
m.set(CONTROL_AE_COMPENSATION_STEP, ParamsUtils.createRational(step));
}
+
+ /*
+ * control.aeLockAvailable
+ */
+ {
+ boolean aeLockAvailable = p.isAutoExposureLockSupported();
+
+ m.set(CONTROL_AE_LOCK_AVAILABLE, aeLockAvailable);
+ }
}
@@ -571,6 +580,16 @@
Log.v(TAG, "mapControlAwb - control.awbAvailableModes set to " +
ListUtils.listToString(awbAvail));
}
+
+
+ /*
+ * control.awbLockAvailable
+ */
+ {
+ boolean awbLockAvailable = p.isAutoWhiteBalanceLockSupported();
+
+ m.set(CONTROL_AWB_LOCK_AVAILABLE, awbLockAvailable);
+ }
}
}
@@ -618,17 +637,44 @@
/*
* android.control.availableSceneModes
*/
+ int maxNumDetectedFaces = p.getMaxNumDetectedFaces();
List<String> sceneModes = p.getSupportedSceneModes();
List<Integer> supportedSceneModes =
ArrayUtils.convertStringListToIntList(sceneModes, sLegacySceneModes, sSceneModes);
- if (supportedSceneModes == null) { // camera1 doesn't support scene mode settings
- supportedSceneModes = new ArrayList<Integer>();
- supportedSceneModes.add(CONTROL_SCENE_MODE_DISABLED); // disabled is always available
+
+ // Special case where the only scene mode listed is AUTO => no scene mode
+ if (sceneModes != null && sceneModes.size() == 1 &&
+ sceneModes.get(0) == Parameters.SCENE_MODE_AUTO) {
+ supportedSceneModes = null;
}
- if (p.getMaxNumDetectedFaces() > 0) { // always supports FACE_PRIORITY when face detecting
- supportedSceneModes.add(CONTROL_SCENE_MODE_FACE_PRIORITY);
+
+ boolean sceneModeSupported = true;
+ if (supportedSceneModes == null && maxNumDetectedFaces == 0) {
+ sceneModeSupported = false;
}
- m.set(CONTROL_AVAILABLE_SCENE_MODES, ArrayUtils.toIntArray(supportedSceneModes));
+
+ if (sceneModeSupported) {
+ if (supportedSceneModes == null) {
+ supportedSceneModes = new ArrayList<Integer>();
+ }
+ if (maxNumDetectedFaces > 0) { // always supports FACE_PRIORITY when face detecting
+ supportedSceneModes.add(CONTROL_SCENE_MODE_FACE_PRIORITY);
+ }
+ // Remove all DISABLED occurrences
+ if (supportedSceneModes.contains(CONTROL_SCENE_MODE_DISABLED)) {
+ while(supportedSceneModes.remove(new Integer(CONTROL_SCENE_MODE_DISABLED))) {}
+ }
+ m.set(CONTROL_AVAILABLE_SCENE_MODES, ArrayUtils.toIntArray(supportedSceneModes));
+ } else {
+ m.set(CONTROL_AVAILABLE_SCENE_MODES, new int[] {CONTROL_SCENE_MODE_DISABLED});
+ }
+
+ /*
+ * android.control.availableModes
+ */
+ m.set(CONTROL_AVAILABLE_MODES, sceneModeSupported ?
+ new int[] { CONTROL_MODE_AUTO, CONTROL_MODE_USE_SCENE_MODE } :
+ new int[] { CONTROL_MODE_AUTO });
}
private static void mapLens(CameraMetadataNative m, Camera.Parameters p) {
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 2099c3f..bf3d9aa 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -366,6 +366,7 @@
public String toSafeString() {
String scheme = getScheme();
String ssp = getSchemeSpecificPart();
+ String authority = null;
if (scheme != null) {
if (scheme.equalsIgnoreCase("tel") || scheme.equalsIgnoreCase("sip")
|| scheme.equalsIgnoreCase("sms") || scheme.equalsIgnoreCase("smsto")
@@ -384,6 +385,9 @@
}
}
return builder.toString();
+ } else if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")) {
+ ssp = null;
+ authority = "//" + getAuthority() + "/...";
}
}
// Not a sensitive scheme, but let's still be conservative about
@@ -397,6 +401,9 @@
if (ssp != null) {
builder.append(ssp);
}
+ if (authority != null) {
+ builder.append(authority);
+ }
return builder.toString();
}
@@ -1742,7 +1749,7 @@
*
* @return normalized Uri (never null)
* @see {@link android.content.Intent#setData}
- * @see {@link #setNormalizedData}
+ * @see {@link android.content.Intent#setDataAndNormalize}
*/
public Uri normalizeScheme() {
String scheme = getScheme();
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 2438071..a929f3d 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -160,7 +160,22 @@
private final EpicenterCallback mEpicenterCallback = new EpicenterCallback() {
@Override
public Rect onGetEpicenter(Transition transition) {
- return mAnchorBounds;
+ final View anchor = mAnchor.get();
+ final View decor = mDecorView;
+ if (anchor == null || decor == null) {
+ return null;
+ }
+
+ final Rect anchorBounds = mAnchorBounds;
+ final int[] anchorLocation = mAnchor.get().getLocationOnScreen();
+ final int[] popupLocation = mDecorView.getLocationOnScreen();
+
+ // Compute the position of the anchor relative to the popup.
+ anchorBounds.set(0, 0, anchor.getWidth(), anchor.getHeight());
+ anchorBounds.offset(anchorLocation[0] - popupLocation[0],
+ anchorLocation[1] - popupLocation[1]);
+
+ return anchorBounds;
}
};
@@ -1494,10 +1509,6 @@
p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
- // Compute the position of the anchor relative to the popup.
- mAnchorBounds.set(0, 0, anchorWidth, anchorHeight);
- mAnchorBounds.offset(mDrawingLocation[0] - p.x, mDrawingLocation[1] - p.y);
-
return onTop;
}
@@ -1573,9 +1584,9 @@
}
/**
- * <p>Dispose of the popup window. This method can be invoked only after
- * {@link #showAsDropDown(android.view.View)} has been executed. Failing that, calling
- * this method will have no effect.</p>
+ * Disposes of the popup window. This method can be invoked only after
+ * {@link #showAsDropDown(android.view.View)} has been executed. Failing
+ * that, calling this method will have no effect.
*
* @see #showAsDropDown(android.view.View)
*/
@@ -1589,6 +1600,9 @@
mIsShowing = false;
if (mExitTransition != null) {
+ // Cache the content view, since it may change without notice.
+ final View contentView = mContentView;
+
mExitTransition.addTarget(mBackgroundView);
mExitTransition.addListener(new Transition.TransitionListenerAdapter() {
@Override
@@ -1596,7 +1610,7 @@
transition.removeListener(this);
transition.removeTarget(mBackgroundView);
- dismissImmediate();
+ dismissImmediate(contentView);
}
});
@@ -1605,7 +1619,11 @@
// Transition to invisible.
mBackgroundView.setVisibility(View.INVISIBLE);
} else {
- dismissImmediate();
+ dismissImmediate(mContentView);
+ }
+
+ if (mOnDismissListener != null) {
+ mOnDismissListener.onDismiss();
}
}
@@ -1613,21 +1631,17 @@
* Removes the popup from the window manager and tears down the supporting
* view hierarchy, if necessary.
*/
- private void dismissImmediate() {
+ private void dismissImmediate(View contentView) {
try {
mWindowManager.removeViewImmediate(mDecorView);
} finally {
mDecorView.removeView(mBackgroundView);
mDecorView = null;
- if (mBackgroundView != mContentView) {
- ((ViewGroup) mBackgroundView).removeView(mContentView);
+ if (mBackgroundView != contentView) {
+ ((ViewGroup) mBackgroundView).removeView(contentView);
}
mBackgroundView = null;
-
- if (mOnDismissListener != null) {
- mOnDismissListener.onDismiss();
- }
}
}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 559d750..d157cdc 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5078,14 +5078,31 @@
<!-- Describes an item (or child) of a LayerDrawable. -->
<declare-styleable name="LayerDrawableItem">
- <!-- Left coordinate of the layer. -->
+ <!-- Left inset to apply to the layer. -->
<attr name="left" />
- <!-- Top coordinate of the layer. -->
+ <!-- Top inset to apply to the layer. -->
<attr name="top" />
- <!-- Right coordinate of the layer. -->
+ <!-- Right inset to apply to the layer. -->
<attr name="right" />
- <!-- Bottom coordinate of the layer. -->
+ <!-- Bottom inset to apply to the layer. -->
<attr name="bottom" />
+ <!-- Start inset to apply to the layer. Overrides {@code left} or
+ {@code right} depending on layout direction. -->
+ <attr name="start" format="dimension" />
+ <!-- End inset to apply to the layer. Overrides {@code left} or
+ {@code right} depending on layout direction. -->
+ <attr name="end" format="dimension" />
+ <!-- Width of the layer. Defaults to the layer's intrinsic width. -->
+ <attr name="width" />
+ <!-- Height of the layer. Defaults to the layer's intrinsic height -->
+ <attr name="height" />
+ <!-- Gravity used to align the layer within its container. If no value
+ is specified, the default behavior depends on whether an explicit
+ width or height has been set, If no dimension is set, gravity in
+ that direction defaults to {@code fill_horizontal} or
+ {@code fill_vertical}; otherwise, it defaults to {@code left} or
+ {@code top}. -->
+ <attr name="gravity" />
<!-- Drawable used to render the layer. -->
<attr name="drawable" />
<!-- Identifier of the layer. This can be used to retrieve the layer
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 11cc181..3bf9631 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2611,6 +2611,8 @@
<public type="attr" name="trackTint" />
<public type="attr" name="trackTintMode" />
<public type="attr" name="resizeableActivity" />
+ <public type="attr" name="start" />
+ <public type="attr" name="end" />
<public type="style" name="Widget.Material.Button.Colored" />
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index daf4427..505bd1d 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -29,6 +29,8 @@
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.LayoutDirection;
+import android.view.Gravity;
import android.view.View;
import com.android.internal.R;
@@ -54,6 +56,11 @@
* @attr ref android.R.styleable#LayerDrawableItem_top
* @attr ref android.R.styleable#LayerDrawableItem_right
* @attr ref android.R.styleable#LayerDrawableItem_bottom
+ * @attr ref android.R.styleable#LayerDrawableItem_start
+ * @attr ref android.R.styleable#LayerDrawableItem_end
+ * @attr ref android.R.styleable#LayerDrawableItem_width
+ * @attr ref android.R.styleable#LayerDrawableItem_height
+ * @attr ref android.R.styleable#LayerDrawableItem_gravity
* @attr ref android.R.styleable#LayerDrawableItem_drawable
* @attr ref android.R.styleable#LayerDrawableItem_id
*/
@@ -73,6 +80,9 @@
*/
public static final int PADDING_MODE_STACK = 1;
+ /** Value used for undefined start and end insets. */
+ private static final int UNDEFINED_INSET = Integer.MIN_VALUE;
+
LayerState mLayerState;
private int mOpacityOverride = PixelFormat.UNKNOWN;
@@ -231,6 +241,16 @@
R.styleable.LayerDrawableItem_right, layer.mInsetR);
layer.mInsetB = a.getDimensionPixelOffset(
R.styleable.LayerDrawableItem_bottom, layer.mInsetB);
+ layer.mInsetS = a.getDimensionPixelOffset(
+ R.styleable.LayerDrawableItem_start, layer.mInsetS);
+ layer.mInsetE = a.getDimensionPixelOffset(
+ R.styleable.LayerDrawableItem_end, layer.mInsetE);
+ layer.mWidth = a.getDimensionPixelSize(
+ R.styleable.LayerDrawableItem_width, layer.mWidth);
+ layer.mHeight = a.getDimensionPixelSize(
+ R.styleable.LayerDrawableItem_height, layer.mHeight);
+ layer.mGravity = a.getInteger(
+ R.styleable.LayerDrawableItem_gravity, layer.mGravity);
layer.mId = a.getResourceId(R.styleable.LayerDrawableItem_id, layer.mId);
final Drawable dr = a.getDrawable(R.styleable.LayerDrawableItem_drawable);
@@ -447,6 +467,89 @@
}
/**
+ * Sets an explicit size for the specified layer.
+ * <p>
+ * <strong>Note:</strong> Setting an explicit layer size changes the
+ * default layer gravity behavior. See {@link #setLayerGravity(int, int)}
+ * for more information.
+ *
+ * @param index the index of the drawable to adjust
+ * @param w width in pixels, or -1 to use the intrinsic width
+ * @param h height in pixels, or -1 to use the intrinsic height
+ *
+ * @see #getLayerWidth(int)
+ * @see #getLayerHeight(int)
+ * @attr ref android.R.styleable#LayerDrawableItem_width
+ * @attr ref android.R.styleable#LayerDrawableItem_height
+ */
+ public void setLayerSize(int index, int w, int h) {
+ final ChildDrawable childDrawable = mLayerState.mChildren[index];
+ childDrawable.mWidth = w;
+ childDrawable.mHeight = h;
+ }
+
+ /**
+ * @param index the index of the drawable to adjust
+ * @return the explicit width of the layer, or -1 if not specified
+ *
+ * @see #setLayerSize(int, int, int)
+ * @attr ref android.R.styleable#LayerDrawableItem_width
+ */
+ public int getLayerWidth(int index) {
+ final ChildDrawable childDrawable = mLayerState.mChildren[index];
+ return childDrawable.mWidth;
+ }
+
+ /**
+ * @param index the index of the drawable to adjust
+ * @return the explicit height of the layer, or -1 if not specified
+ *
+ * @see #setLayerSize(int, int, int)
+ * @attr ref android.R.styleable#LayerDrawableItem_height
+ */
+ public int getLayerHeight(int index) {
+ final ChildDrawable childDrawable = mLayerState.mChildren[index];
+ return childDrawable.mHeight;
+ }
+
+ /**
+ * Sets the gravity used to position or stretch the specified layer within
+ * its container. Gravity is applied after any layer insets (see
+ * {@link #setLayerInset(int, int, int, int, int)}) or padding (see
+ * {@link #setPaddingMode(int)}).
+ * <p>
+ * If gravity is specified as {@link Gravity#NO_GRAVITY}, the default
+ * behavior depends on whether an explicit width or height has been set
+ * (see {@link #setLayerSize(int, int, int)}), If a dimension is not set,
+ * gravity in that direction defaults to {@link Gravity#FILL_HORIZONTAL} or
+ * {@link Gravity#FILL_VERTICAL}; otherwise, gravity in that direction
+ * defaults to {@link Gravity#LEFT} or {@link Gravity#TOP}.
+ *
+ * @param index the index of the drawable to adjust
+ * @param gravity the gravity to set for the layer
+ *
+ * @see #getLayerGravity(int)
+ * @attr ref android.R.styleable#LayerDrawableItem_gravity
+ */
+ public void setLayerGravity(int index, int gravity) {
+ final ChildDrawable childDrawable = mLayerState.mChildren[index];
+ childDrawable.mGravity = gravity;
+ }
+
+ /**
+ * @param index the index of the layer
+ * @return the gravity used to position or stretch the specified layer
+ * within its container
+ *
+ * @see #setLayerGravity(int, int)
+ * @attr ref android.R.styleable#LayerDrawableItem_gravity
+ */
+ public int getLayerGravity(int index) {
+ final ChildDrawable childDrawable = mLayerState.mChildren[index];
+ return childDrawable.mGravity;
+ }
+
+ /**
* Specifies the insets in pixels for the drawable at the specified index.
*
* @param index the index of the drawable to adjust
@@ -454,13 +557,43 @@
* @param t number of pixels to add to the top bound
* @param r number of pixels to subtract from the right bound
* @param b number of pixels to subtract from the bottom bound
+ *
+ * @attr ref android.R.styleable#LayerDrawableItem_left
+ * @attr ref android.R.styleable#LayerDrawableItem_top
+ * @attr ref android.R.styleable#LayerDrawableItem_right
+ * @attr ref android.R.styleable#LayerDrawableItem_bottom
*/
public void setLayerInset(int index, int l, int t, int r, int b) {
+ setLayerInsetInternal(index, l, t, r, b, UNDEFINED_INSET, UNDEFINED_INSET);
+ }
+
+ /**
+ * Specifies the relative insets in pixels for the drawable at the
+ * specified index.
+ *
+ * @param index the index of the drawable to adjust
+ * @param s number of pixels to inset from the start bound
+ * @param t number of pixels to inset from the top bound
+ * @param e number of pixels to inset from the end bound
+ * @param b number of pixels to inset from the bottom bound
+ *
+ * @attr ref android.R.styleable#LayerDrawableItem_start
+ * @attr ref android.R.styleable#LayerDrawableItem_top
+ * @attr ref android.R.styleable#LayerDrawableItem_end
+ * @attr ref android.R.styleable#LayerDrawableItem_bottom
+ */
+ public void setLayerInsetRelative(int index, int s, int t, int e, int b) {
+ setLayerInsetInternal(index, 0, t, 0, b, s, e);
+ }
+
+ private void setLayerInsetInternal(int index, int l, int t, int r, int b, int s, int e) {
final ChildDrawable childDrawable = mLayerState.mChildren[index];
childDrawable.mInsetL = l;
childDrawable.mInsetT = t;
childDrawable.mInsetR = r;
childDrawable.mInsetB = b;
+ childDrawable.mInsetS = s;
+ childDrawable.mInsetE = e;
}
/**
@@ -770,7 +903,7 @@
}
if (paddingChanged) {
- onBoundsChange(getBounds());
+ updateLayerBounds(getBounds());
}
return changed;
@@ -795,7 +928,7 @@
}
if (paddingChanged) {
- onBoundsChange(getBounds());
+ updateLayerBounds(getBounds());
}
return changed;
@@ -803,18 +936,50 @@
@Override
protected void onBoundsChange(Rect bounds) {
+ updateLayerBounds(bounds);
+ }
+
+ private void updateLayerBounds(Rect bounds) {
int padL = 0;
int padT = 0;
int padR = 0;
int padB = 0;
+ final Rect outRect = mTmpRect;
+ final int layoutDirection = getLayoutDirection();
final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
final ChildDrawable[] array = mLayerState.mChildren;
final int N = mLayerState.mNum;
for (int i = 0; i < N; i++) {
final ChildDrawable r = array[i];
- r.mDrawable.setBounds(bounds.left + r.mInsetL + padL, bounds.top + r.mInsetT + padT,
- bounds.right - r.mInsetR - padR, bounds.bottom - r.mInsetB - padB);
+ final Drawable d = r.mDrawable;
+ final Rect container = d.getBounds();
+
+ // Take the resolved layout direction into account. If start / end
+ // padding are defined, they will be resolved (hence overriding) to
+ // left / right or right / left depending on the resolved layout
+ // direction. If start / end padding are not defined, use the
+ // left / right ones.
+ final int insetL, insetR;
+ if (layoutDirection == LayoutDirection.RTL) {
+ insetL = r.mInsetE == UNDEFINED_INSET ? r.mInsetL : r.mInsetE;
+ insetR = r.mInsetS == UNDEFINED_INSET ? r.mInsetR : r.mInsetS;
+ } else {
+ insetL = r.mInsetS == UNDEFINED_INSET ? r.mInsetL : r.mInsetS;
+ insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE;
+ }
+
+ // Establish containing region based on aggregate padding and
+ // requested insets for the current layer.
+ container.set(bounds.left + insetL + padL, bounds.top + r.mInsetT + padT,
+ bounds.right - insetR - padR, bounds.bottom - r.mInsetB - padB);
+
+ // Apply resolved gravity to drawable based on resolved size.
+ final int gravity = resolveGravity(r.mGravity, r.mWidth, r.mHeight);
+ final int w = r.mWidth < 0 ? d.getIntrinsicWidth() : r.mWidth;
+ final int h = r.mHeight < 0 ? d.getIntrinsicHeight() : r.mHeight;
+ Gravity.apply(gravity, w, h, container, outRect, layoutDirection);
+ d.setBounds(outRect);
if (nest) {
padL += mPaddingL[i];
@@ -825,6 +990,38 @@
}
}
+ /**
+ * Resolves layer gravity given explicit gravity and dimensions.
+ * <p>
+ * If the client hasn't specified a gravity but has specified an explicit
+ * dimension, defaults to START or TOP. Otherwise, defaults to FILL to
+ * preserve legacy behavior.
+ *
+ * @param gravity
+ * @param width
+ * @param height
+ * @return
+ */
+ private int resolveGravity(int gravity, int width, int height) {
+ if (!Gravity.isHorizontal(gravity)) {
+ if (width < 0) {
+ gravity |= Gravity.FILL_HORIZONTAL;
+ } else {
+ gravity |= Gravity.START;
+ }
+ }
+
+ if (!Gravity.isVertical(gravity)) {
+ if (height < 0) {
+ gravity |= Gravity.FILL_VERTICAL;
+ } else {
+ gravity |= Gravity.TOP;
+ }
+ }
+
+ return gravity;
+ }
+
@Override
public int getIntrinsicWidth() {
int width = -1;
@@ -836,7 +1033,8 @@
final int N = mLayerState.mNum;
for (int i = 0; i < N; i++) {
final ChildDrawable r = array[i];
- final int w = r.mDrawable.getIntrinsicWidth() + r.mInsetL + r.mInsetR + padL + padR;
+ final int minWidth = r.mWidth < 0 ? r.mDrawable.getIntrinsicWidth() : r.mWidth;
+ final int w = minWidth + r.mInsetL + r.mInsetR + padL + padR;
if (w > width) {
width = w;
}
@@ -861,7 +1059,8 @@
final int N = mLayerState.mNum;
for (int i = 0; i < N; i++) {
final ChildDrawable r = array[i];
- int h = r.mDrawable.getIntrinsicHeight() + r.mInsetT + r.mInsetB + padT + padB;
+ final int minHeight = r.mHeight < 0 ? r.mDrawable.getIntrinsicHeight() : r.mHeight;
+ final int h = minHeight + r.mInsetT + r.mInsetB + padT + padB;
if (h > height) {
height = h;
}
@@ -948,18 +1147,24 @@
/** @hide */
@Override
public void setLayoutDirection(int layoutDirection) {
+ super.setLayoutDirection(layoutDirection);
final ChildDrawable[] array = mLayerState.mChildren;
final int N = mLayerState.mNum;
for (int i = 0; i < N; i++) {
array[i].mDrawable.setLayoutDirection(layoutDirection);
}
- super.setLayoutDirection(layoutDirection);
+ updateLayerBounds(getBounds());
}
static class ChildDrawable {
public Drawable mDrawable;
public int[] mThemeAttrs;
public int mInsetL, mInsetT, mInsetR, mInsetB;
+ public int mInsetS = UNDEFINED_INSET;
+ public int mInsetE = UNDEFINED_INSET;
+ public int mWidth = -1;
+ public int mHeight = -1;
+ public int mGravity = Gravity.NO_GRAVITY;
public int mId = View.NO_ID;
ChildDrawable() {
@@ -981,6 +1186,11 @@
mInsetT = orig.mInsetT;
mInsetR = orig.mInsetR;
mInsetB = orig.mInsetB;
+ mInsetS = orig.mInsetS;
+ mInsetE = orig.mInsetE;
+ mWidth = orig.mWidth;
+ mHeight = orig.mHeight;
+ mGravity = orig.mGravity;
mId = orig.mId;
}
}
diff --git a/location/java/android/location/GpsSatellite.java b/location/java/android/location/GpsSatellite.java
index 17af4a6..820f5746 100644
--- a/location/java/android/location/GpsSatellite.java
+++ b/location/java/android/location/GpsSatellite.java
@@ -40,13 +40,17 @@
* cached GpsStatus instance to the client's copy.
*/
void setStatus(GpsSatellite satellite) {
- mValid = satellite.mValid;
- mHasEphemeris = satellite.mHasEphemeris;
- mHasAlmanac = satellite.mHasAlmanac;
- mUsedInFix = satellite.mUsedInFix;
- mSnr = satellite.mSnr;
- mElevation = satellite.mElevation;
- mAzimuth = satellite.mAzimuth;
+ if (satellite == null) {
+ mValid = false;
+ } else {
+ mValid = satellite.mValid;
+ mHasEphemeris = satellite.mHasEphemeris;
+ mHasAlmanac = satellite.mHasAlmanac;
+ mUsedInFix = satellite.mUsedInFix;
+ mSnr = satellite.mSnr;
+ mElevation = satellite.mElevation;
+ mAzimuth = satellite.mAzimuth;
+ }
}
/**
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 4af55a6..323f326 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -16,6 +16,8 @@
package android.location;
+import android.util.SparseArray;
+
import java.util.Iterator;
import java.util.NoSuchElementException;
@@ -29,20 +31,24 @@
/* These package private values are modified by the LocationManager class */
private int mTimeToFirstFix;
- private GpsSatellite mSatellites[] = new GpsSatellite[NUM_SATELLITES];
+ private final SparseArray<GpsSatellite> mSatellites = new SparseArray<>();
private final class SatelliteIterator implements Iterator<GpsSatellite> {
- private GpsSatellite[] mSatellites;
- int mIndex = 0;
+ private final SparseArray<GpsSatellite> mSatellites;
+ private final int mSatellitesCount;
- SatelliteIterator(GpsSatellite[] satellites) {
+ private int mIndex = 0;
+
+ SatelliteIterator(SparseArray<GpsSatellite> satellites) {
mSatellites = satellites;
+ mSatellitesCount = satellites.size();
}
public boolean hasNext() {
- for (int i = mIndex; i < mSatellites.length; i++) {
- if (mSatellites[i].mValid) {
+ for (; mIndex < mSatellitesCount; ++mIndex) {
+ GpsSatellite satellite = mSatellites.valueAt(mIndex);
+ if (satellite.mValid) {
return true;
}
}
@@ -50,8 +56,9 @@
}
public GpsSatellite next() {
- while (mIndex < mSatellites.length) {
- GpsSatellite satellite = mSatellites[mIndex++];
+ while (mIndex < mSatellitesCount) {
+ GpsSatellite satellite = mSatellites.valueAt(mIndex);
+ ++mIndex;
if (satellite.mValid) {
return satellite;
}
@@ -106,7 +113,7 @@
* <li> {@link GpsStatus#GPS_EVENT_SATELLITE_STATUS}
* </ul>
*
- * When this method is called, the client should call
+ * When this method is called, the client should call
* {@link LocationManager#getGpsStatus} to get additional
* status information.
*
@@ -127,11 +134,8 @@
void onNmeaReceived(long timestamp, String nmea);
}
- GpsStatus() {
- for (int i = 0; i < mSatellites.length; i++) {
- mSatellites[i] = new GpsSatellite(i + 1);
- }
- }
+ // For API-compat a public ctor() is not available
+ GpsStatus() {}
/**
* Used internally within {@link LocationManager} to copy GPS status
@@ -141,18 +145,17 @@
synchronized void setStatus(int svCount, int[] prns, float[] snrs,
float[] elevations, float[] azimuths, int ephemerisMask,
int almanacMask, int usedInFixMask) {
- int i;
+ clearSatellites();
+ for (int i = 0; i < svCount; i++) {
+ int prn = prns[i];
+ int prnShift = (1 << (prn - 1));
+ if (prn > 0 && prn <= NUM_SATELLITES) {
+ GpsSatellite satellite = mSatellites.get(prn);
+ if (satellite == null) {
+ satellite = new GpsSatellite(prn);
+ mSatellites.put(prn, satellite);
+ }
- for (i = 0; i < mSatellites.length; i++) {
- mSatellites[i].mValid = false;
- }
-
- for (i = 0; i < svCount; i++) {
- int prn = prns[i] - 1;
- int prnShift = (1 << prn);
- if (prn >= 0 && prn < mSatellites.length) {
- GpsSatellite satellite = mSatellites[prn];
-
satellite.mValid = true;
satellite.mSnr = snrs[i];
satellite.mElevation = elevations[i];
@@ -172,10 +175,38 @@
*/
void setStatus(GpsStatus status) {
mTimeToFirstFix = status.getTimeToFirstFix();
+ clearSatellites();
- for (int i = 0; i < mSatellites.length; i++) {
- mSatellites[i].setStatus(status.mSatellites[i]);
- }
+ SparseArray<GpsSatellite> otherSatellites = status.mSatellites;
+ int otherSatellitesCount = otherSatellites.size();
+ int satelliteIndex = 0;
+ // merge both sparse arrays, note that we have already invalidated the elements in the
+ // receiver array
+ for (int i = 0; i < otherSatellitesCount; ++i) {
+ GpsSatellite otherSatellite = otherSatellites.valueAt(i);
+ int otherSatellitePrn = otherSatellite.getPrn();
+
+ int satellitesCount = mSatellites.size();
+ while (satelliteIndex < satellitesCount
+ && mSatellites.valueAt(satelliteIndex).getPrn() < otherSatellitePrn) {
+ ++satelliteIndex;
+ }
+
+ if (satelliteIndex < mSatellites.size()) {
+ GpsSatellite satellite = mSatellites.valueAt(satelliteIndex);
+ if (satellite.getPrn() == otherSatellitePrn) {
+ satellite.setStatus(otherSatellite);
+ } else {
+ satellite = new GpsSatellite(otherSatellitePrn);
+ satellite.setStatus(otherSatellite);
+ mSatellites.put(otherSatellitePrn, satellite);
+ }
+ } else {
+ GpsSatellite satellite = new GpsSatellite(otherSatellitePrn);
+ satellite.setStatus(otherSatellite);
+ mSatellites.append(otherSatellitePrn, satellite);
+ }
+ }
}
void setTimeToFirstFix(int ttff) {
@@ -183,7 +214,7 @@
}
/**
- * Returns the time required to receive the first fix since the most recent
+ * Returns the time required to receive the first fix since the most recent
* restart of the GPS engine.
*
* @return time to first fix in milliseconds
@@ -211,4 +242,12 @@
public int getMaxSatellites() {
return NUM_SATELLITES;
}
+
+ private void clearSatellites() {
+ int satellitesCount = mSatellites.size();
+ for (int i = 0; i < satellitesCount; i++) {
+ GpsSatellite satellite = mSatellites.valueAt(i);
+ satellite.mValid = false;
+ }
+ }
}
diff --git a/location/tests/locationtests/src/android/location/GpsStatusTest.java b/location/tests/locationtests/src/android/location/GpsStatusTest.java
new file mode 100644
index 0000000..4808faf
--- /dev/null
+++ b/location/tests/locationtests/src/android/location/GpsStatusTest.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.location;
+
+import junit.framework.TestCase;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link GpsStatus}.
+ */
+@SmallTest
+public class GpsStatusTest extends TestCase {
+
+ private static final int MAX_VALUE = 250;
+
+ private final Random mRandom = new Random();
+
+ private GpsStatus mStatus;
+ private int mCount;
+ private int[] mPrns;
+ private float[] mSnrs;
+ private float[] mElevations;
+ private float[] mAzimuth;
+ private int mEphemerisMask;
+ private int mAlmanacMask;
+ private int mUsedInFixMask;
+
+ public void setUp() throws Exception {
+ super.setUp();
+ mStatus = createGpsStatus();
+ generateSatellitesData(generateInt());
+ }
+
+ public void testEmptyGpsStatus() throws Exception {
+ verifyIsEmpty(mStatus);
+ }
+
+ public void testGpsStatusIterator() throws Exception {
+ generateSatellitesData(2);
+ setSatellites(mStatus);
+ Iterator<GpsSatellite> iterator = mStatus.getSatellites().iterator();
+ assertTrue("hasNext(1)", iterator.hasNext());
+ assertTrue("hasNext(1) does not overflow", iterator.hasNext());
+ GpsSatellite satellite1 = iterator.next();
+ assertNotNull("satellite", satellite1);
+ assertTrue("hasNext(2)", iterator.hasNext());
+ assertTrue("hasNext(2) does not overflow", iterator.hasNext());
+ GpsSatellite satellite2 = iterator.next();
+ assertNotNull("satellite", satellite2);
+ assertFalse("hasNext() no elements", iterator.hasNext());
+ }
+
+ public void testTtff() throws Exception {
+ int testTtff = generateInt();
+ set(mStatus, testTtff);
+ verifyTtff(mStatus, testTtff);
+ }
+
+ public void testCopyTtff() throws Exception {
+ int testTtff = generateInt();
+ verifyTtff(mStatus, 0);
+
+ GpsStatus otherStatus = createGpsStatus();
+ set(otherStatus, testTtff);
+ verifyTtff(otherStatus, testTtff);
+
+ set(mStatus, otherStatus);
+ verifyTtff(mStatus, testTtff);
+ }
+
+ public void testSetSatellites() throws Exception {
+ setSatellites(mStatus);
+ verifySatellites(mStatus);
+ }
+
+ public void testCopySatellites() throws Exception {
+ verifyIsEmpty(mStatus);
+
+ GpsStatus otherStatus = createGpsStatus();
+ setSatellites(otherStatus);
+ verifySatellites(otherStatus);
+
+ set(mStatus, otherStatus);
+ verifySatellites(mStatus);
+ }
+
+ public void testOverrideSatellites() throws Exception {
+ setSatellites(mStatus);
+ verifySatellites(mStatus);
+
+ GpsStatus otherStatus = createGpsStatus();
+ generateSatellitesData(mCount, true /* reusePrns */);
+ setSatellites(otherStatus);
+ verifySatellites(otherStatus);
+
+ set(mStatus, otherStatus);
+ verifySatellites(mStatus);
+ }
+
+ public void testAddSatellites() throws Exception {
+ int count = 10;
+ generateSatellitesData(count);
+ setSatellites(mStatus);
+ verifySatellites(mStatus);
+
+ GpsStatus otherStatus = createGpsStatus();
+ generateSatellitesData(count);
+ setSatellites(otherStatus);
+ verifySatellites(otherStatus);
+
+ set(mStatus, otherStatus);
+ verifySatellites(mStatus);
+ }
+
+ public void testAddMoreSatellites() throws Exception {
+ int count = 25;
+ generateSatellitesData(count);
+ setSatellites(mStatus);
+ verifySatellites(mStatus);
+
+ GpsStatus otherStatus = createGpsStatus();
+ generateSatellitesData(count * 2);
+ setSatellites(otherStatus);
+ verifySatellites(otherStatus);
+
+ set(mStatus, otherStatus);
+ verifySatellites(mStatus);
+ }
+
+ public void testAddLessSatellites() throws Exception {
+ int count = 25;
+ generateSatellitesData(count * 2);
+ setSatellites(mStatus);
+ verifySatellites(mStatus);
+
+ GpsStatus otherStatus = createGpsStatus();
+ generateSatellitesData(count);
+ setSatellites(otherStatus);
+ verifySatellites(otherStatus);
+
+ set(mStatus, otherStatus);
+ verifySatellites(mStatus);
+ }
+
+ private static void verifyIsEmpty(GpsStatus status) {
+ verifySatelliteCount(status, 0);
+ verifyTtff(status, 0);
+ }
+
+ private static void verifySatelliteCount(GpsStatus status, int expectedCount) {
+ int satellites = 0;
+ for (GpsSatellite s : status.getSatellites()) {
+ ++satellites;
+ }
+ assertEquals("GpsStatus::SatelliteCount", expectedCount, satellites);
+ }
+
+ private void verifySatellites(GpsStatus status) {
+ verifySatelliteCount(status, mCount);
+ verifySatellites(status, mCount, mPrns, mSnrs, mElevations, mAzimuth, mEphemerisMask,
+ mAlmanacMask, mUsedInFixMask);
+ }
+
+ private static void verifySatellites(
+ GpsStatus status,
+ int count,
+ int[] prns,
+ float[] snrs,
+ float[] elevations,
+ float[] azimuth,
+ int ephemerisMask,
+ int almanacMask,
+ int usedInFixMask) {
+ for (int i = 0; i < count; ++i) {
+ int prn = prns[i];
+ GpsSatellite satellite = getSatellite(status, prn);
+ assertNotNull(getSatelliteAssertInfo(i, prn, "non-null"), satellite);
+ assertEquals(getSatelliteAssertInfo(i, prn, "Snr"), snrs[i], satellite.getSnr());
+ assertEquals(
+ getSatelliteAssertInfo(i, prn, "Elevation"),
+ elevations[i],
+ satellite.getElevation());
+ assertEquals(
+ getSatelliteAssertInfo(i, prn, "Azimuth"),
+ azimuth[i],
+ satellite.getAzimuth());
+ int prnShift = 1 << (prn - 1);
+ assertEquals(
+ getSatelliteAssertInfo(i, prn, "ephemeris"),
+ (ephemerisMask & prnShift) != 0,
+ satellite.hasEphemeris());
+ assertEquals(
+ getSatelliteAssertInfo(i, prn, "almanac"),
+ (almanacMask & prnShift) != 0,
+ satellite.hasAlmanac());
+ assertEquals(
+ getSatelliteAssertInfo(i, prn, "usedInFix"),
+ (usedInFixMask & prnShift) != 0,
+ satellite.usedInFix());
+ }
+ }
+
+ private static void verifyTtff(GpsStatus status, int expectedTtff) {
+ assertEquals("GpsStatus::TTFF", expectedTtff, status.getTimeToFirstFix());
+ }
+
+ private static GpsStatus createGpsStatus() throws Exception {
+ Constructor<GpsStatus> ctor = GpsStatus.class.getDeclaredConstructor();
+ ctor.setAccessible(true);
+ return ctor.newInstance();
+ }
+
+ private static void set(GpsStatus status, int ttff) throws Exception {
+ Class<?> statusClass = status.getClass();
+ Method setTtff = statusClass.getDeclaredMethod("setTimeToFirstFix", Integer.TYPE);
+ setTtff.setAccessible(true);
+ setTtff.invoke(status, ttff);
+ }
+
+ private static void set(GpsStatus status, GpsStatus statusToSet) throws Exception {
+ Class<?> statusClass = status.getClass();
+ Method setStatus = statusClass.getDeclaredMethod("setStatus", statusClass);
+ setStatus.setAccessible(true);
+ setStatus.invoke(status, statusToSet);
+ }
+
+ private void setSatellites(GpsStatus status) throws Exception {
+ set(status, mCount, mPrns, mSnrs, mElevations, mAzimuth, mEphemerisMask, mAlmanacMask,
+ mUsedInFixMask);
+ }
+
+ private static void set(
+ GpsStatus status,
+ int count,
+ int[] prns,
+ float[] snrs,
+ float[] elevations,
+ float[] azimuth,
+ int ephemerisMask,
+ int almanacMask,
+ int usedInFixMask) throws Exception {
+ Class<?> statusClass = status.getClass();
+ Class<?> intClass = Integer.TYPE;
+ Class<?> floatArrayClass = Class.forName("[F");
+ Method setStatus = statusClass.getDeclaredMethod(
+ "setStatus",
+ intClass,
+ Class.forName("[I"),
+ floatArrayClass,
+ floatArrayClass,
+ floatArrayClass,
+ intClass,
+ intClass,
+ intClass);
+ setStatus.setAccessible(true);
+ setStatus.invoke(
+ status,
+ count,
+ prns,
+ snrs,
+ elevations,
+ azimuth,
+ ephemerisMask,
+ almanacMask,
+ usedInFixMask);
+ }
+
+ private int generateInt() {
+ return mRandom.nextInt(MAX_VALUE) + 1;
+ }
+
+ private int[] generateIntArray(int count) {
+ Set<Integer> generatedPrns = new HashSet<>();
+ int[] array = new int[count];
+ for(int i = 0; i < count; ++i) {
+ int generated;
+ do {
+ generated = generateInt();
+ } while (generatedPrns.contains(generated));
+ array[i] = generated;
+ generatedPrns.add(generated);
+ }
+ return array;
+ }
+
+ private float[] generateFloatArray(int count) {
+ float[] array = new float[count];
+ for(int i = 0; i < count; ++i) {
+ array[i] = generateInt();
+ }
+ return array;
+ }
+
+ private int generateMask(int[] prns) {
+ int mask = 0;
+ int prnsLength = prns.length;
+ for (int i = 0; i < prnsLength; ++i) {
+ if (mRandom.nextBoolean()) {
+ mask |= 1 << (prns[i] - 1);
+ }
+ }
+ return mask;
+ }
+
+ private void generateSatellitesData(int count) {
+ generateSatellitesData(count, false /* reusePrns */);
+ }
+
+ private void generateSatellitesData(int count, boolean reusePrns) {
+ mCount = count;
+ if (!reusePrns) {
+ mPrns = generateIntArray(count);
+ }
+ mSnrs = generateFloatArray(count);
+ mElevations = generateFloatArray(count);
+ mAzimuth = generateFloatArray(count);
+ mEphemerisMask = generateMask(mPrns);
+ mAlmanacMask = generateMask(mPrns);
+ mUsedInFixMask = generateMask(mPrns);
+ }
+
+ private static GpsSatellite getSatellite(GpsStatus status, int prn) {
+ for (GpsSatellite satellite : status.getSatellites()) {
+ if (satellite.getPrn() == prn) {
+ return satellite;
+ }
+ }
+ return null;
+ }
+
+ private static String getSatelliteAssertInfo(int index, int prn, String param) {
+ return String.format("Satellite::%s [i=%d, prn=%d]", param, index, prn);
+ }
+}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 489f552..ca242e4 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -709,7 +709,13 @@
}
}
- /** @hide */
+ /**
+ * @hide
+ * Only use to get which stream type should be used for volume control, NOT for audio playback
+ * (all audio playback APIs are supposed to take AudioAttributes as input parameters)
+ * @param aa non-null AudioAttributes.
+ * @return a valid stream type for volume control that matches the attributes.
+ */
public static int toLegacyStreamType(AudioAttributes aa) {
// flags to stream type mapping
if ((aa.getFlags() & FLAG_AUDIBILITY_ENFORCED) == FLAG_AUDIBILITY_ENFORCED) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index f448dc2..986ae46 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -46,9 +46,9 @@
import android.util.Log;
import android.view.KeyEvent;
-import java.util.HashMap;
import java.util.ArrayList;
-
+import java.util.HashMap;
+import java.util.Iterator;
/**
* AudioManager provides access to volume and ringer mode control.
@@ -3598,11 +3598,13 @@
newPorts.clear();
status = AudioSystem.listAudioPorts(newPorts, portGeneration);
if (status != SUCCESS) {
+ Log.w(TAG, "updateAudioPortCache: listAudioPorts failed");
return status;
}
newPatches.clear();
status = AudioSystem.listAudioPatches(newPatches, patchGeneration);
if (status != SUCCESS) {
+ Log.w(TAG, "updateAudioPortCache: listAudioPatches failed");
return status;
}
} while (patchGeneration[0] != portGeneration[0]);
@@ -3611,20 +3613,35 @@
for (int j = 0; j < newPatches.get(i).sources().length; j++) {
AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sources()[j],
newPorts);
- if (portCfg == null) {
- return ERROR;
- }
newPatches.get(i).sources()[j] = portCfg;
}
for (int j = 0; j < newPatches.get(i).sinks().length; j++) {
AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sinks()[j],
newPorts);
- if (portCfg == null) {
- return ERROR;
- }
newPatches.get(i).sinks()[j] = portCfg;
}
}
+ for (Iterator<AudioPatch> i = newPatches.iterator(); i.hasNext(); ) {
+ AudioPatch newPatch = i.next();
+ boolean hasInvalidPort = false;
+ for (AudioPortConfig portCfg : newPatch.sources()) {
+ if (portCfg == null) {
+ hasInvalidPort = true;
+ break;
+ }
+ }
+ for (AudioPortConfig portCfg : newPatch.sinks()) {
+ if (portCfg == null) {
+ hasInvalidPort = true;
+ break;
+ }
+ }
+ if (hasInvalidPort) {
+ // Temporarily remove patches with invalid ports. One who created the patch
+ // is responsible for dealing with the port change.
+ i.remove();
+ }
+ }
sAudioPortsCached = newPorts;
sAudioPatchesCached = newPatches;
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 5b92266..bc9722e 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -756,8 +756,9 @@
* </p><p>
* Note that this sub-directory also supports opening the logo as an asset file in write
* mode. Callers can create or replace the primary logo associated with this channel by
- * opening the asset file and writing the full-size photo contents into it. When the file
- * is closed, the image will be parsed, sized down if necessary, and stored.
+ * opening the asset file and writing the full-size photo contents into it. (Make sure there
+ * is no padding around the logo image.) When the file is closed, the image will be parsed,
+ * sized down if necessary, and stored.
* </p><p>
* Usage example:
* <pre>
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index f900d0d..87f78c1 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -28,6 +28,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
+import android.net.ProxyInfo;
import android.net.TrafficStats;
import android.net.Uri;
import android.net.wifi.WifiInfo;
@@ -656,12 +657,36 @@
int httpResponseCode = 599;
try {
URL url = new URL("http", mServer, "/generate_204");
+ // On networks with a PAC instead of fetching a URL that should result in a 204
+ // reponse, we instead simply fetch the PAC script. This is done for a few reasons:
+ // 1. At present our PAC code does not yet handle multiple PACs on multiple networks
+ // until something like https://android-review.googlesource.com/#/c/115180/ lands.
+ // Network.openConnection() will ignore network-specific PACs and instead fetch
+ // using NO_PROXY. If a PAC is in place, the only fetch we know will succeed with
+ // NO_PROXY is the fetch of the PAC itself.
+ // 2. To proxy the generate_204 fetch through a PAC would require a number of things
+ // happen before the fetch can commence, namely:
+ // a) the PAC script be fetched
+ // b) a PAC script resolver service be fired up and resolve mServer
+ // Network validation could be delayed until these prerequisities are satisifed or
+ // could simply be left to race them. Neither is an optimal solution.
+ // 3. PAC scripts are sometimes used to block or restrict Internet access and may in
+ // fact block fetching of the generate_204 URL which would lead to false negative
+ // results for network validation.
+ boolean fetchPac = false;
+ {
+ final ProxyInfo proxyInfo = mNetworkAgentInfo.linkProperties.getHttpProxy();
+ if (proxyInfo != null && !Uri.EMPTY.equals(proxyInfo.getPacFileUrl())) {
+ url = new URL(proxyInfo.getPacFileUrl().toString());
+ fetchPac = true;
+ }
+ }
if (DBG) {
log("Checking " + url.toString() + " on " +
mNetworkAgentInfo.networkInfo.getExtraInfo());
}
urlConnection = (HttpURLConnection) mNetworkAgentInfo.network.openConnection(url);
- urlConnection.setInstanceFollowRedirects(false);
+ urlConnection.setInstanceFollowRedirects(fetchPac);
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setUseCaches(false);
@@ -695,6 +720,11 @@
httpResponseCode = 204;
}
+ if (httpResponseCode == 200 && fetchPac) {
+ if (DBG) log("PAC fetch 200 response interpreted as 204 response.");
+ httpResponseCode = 204;
+ }
+
sendNetworkConditionsBroadcast(true /* response received */,
httpResponseCode != 204 /* isCaptivePortal */,
requestTimestamp, responseTimestamp);