diff options
Diffstat (limited to 'core/java')
81 files changed, 3066 insertions, 950 deletions
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java index e57be83..f4e4671 100644 --- a/core/java/android/animation/AnimatorInflater.java +++ b/core/java/android/animation/AnimatorInflater.java @@ -370,14 +370,23 @@ public class AnimatorInflater { + " propertyXName or propertyYName is needed for PathData"); } else { Path path = PathParser.createPathFromPathData(pathData); - Keyframe[][] keyframes = PropertyValuesHolder.createKeyframes(path, !getFloats); + PathKeyframes keyframeSet = KeyframeSet.ofPath(path); + Keyframes xKeyframes; + Keyframes yKeyframes; + if (getFloats) { + xKeyframes = keyframeSet.createXFloatKeyframes(); + yKeyframes = keyframeSet.createYFloatKeyframes(); + } else { + xKeyframes = keyframeSet.createXIntKeyframes(); + yKeyframes = keyframeSet.createYIntKeyframes(); + } PropertyValuesHolder x = null; PropertyValuesHolder y = null; if (propertyXName != null) { - x = PropertyValuesHolder.ofKeyframe(propertyXName, keyframes[0]); + x = PropertyValuesHolder.ofKeyframes(propertyXName, xKeyframes); } if (propertyYName != null) { - y = PropertyValuesHolder.ofKeyframe(propertyYName, keyframes[1]); + y = PropertyValuesHolder.ofKeyframes(propertyYName, yKeyframes); } if (x == null) { oa.setValues(y); diff --git a/core/java/android/animation/FloatKeyframeSet.java b/core/java/android/animation/FloatKeyframeSet.java index 2d87e13..12e5862 100644 --- a/core/java/android/animation/FloatKeyframeSet.java +++ b/core/java/android/animation/FloatKeyframeSet.java @@ -30,7 +30,7 @@ import java.util.ArrayList; * TypeEvaluator set for the animation, so that values can be calculated without autoboxing to the * Object equivalents of these primitive types.</p> */ -class FloatKeyframeSet extends KeyframeSet { +class FloatKeyframeSet extends KeyframeSet implements Keyframes.FloatKeyframes { private float firstValue; private float lastValue; private float deltaValue; @@ -58,10 +58,11 @@ class FloatKeyframeSet extends KeyframeSet { } @Override - void invalidateCache() { + public void invalidateCache() { firstTime = true; } + @Override public float getFloatValue(float fraction) { if (mNumKeyframes == 2) { if (firstTime) { @@ -135,5 +136,9 @@ class FloatKeyframeSet extends KeyframeSet { return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue(); } + @Override + public Class getType() { + return Float.class; + } } diff --git a/core/java/android/animation/IntKeyframeSet.java b/core/java/android/animation/IntKeyframeSet.java index ce47e2b..7a5b0ec 100644 --- a/core/java/android/animation/IntKeyframeSet.java +++ b/core/java/android/animation/IntKeyframeSet.java @@ -30,7 +30,7 @@ import java.util.ArrayList; * TypeEvaluator set for the animation, so that values can be calculated without autoboxing to the * Object equivalents of these primitive types.</p> */ -class IntKeyframeSet extends KeyframeSet { +class IntKeyframeSet extends KeyframeSet implements Keyframes.IntKeyframes { private int firstValue; private int lastValue; private int deltaValue; @@ -58,10 +58,11 @@ class IntKeyframeSet extends KeyframeSet { } @Override - void invalidateCache() { + public void invalidateCache() { firstTime = true; } + @Override public int getIntValue(float fraction) { if (mNumKeyframes == 2) { if (firstTime) { @@ -134,5 +135,9 @@ class IntKeyframeSet extends KeyframeSet { return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).intValue(); } + @Override + public Class getType() { + return Integer.class; + } } diff --git a/core/java/android/animation/KeyframeSet.java b/core/java/android/animation/KeyframeSet.java index a3db3a1..fc9bbb1 100644 --- a/core/java/android/animation/KeyframeSet.java +++ b/core/java/android/animation/KeyframeSet.java @@ -21,6 +21,7 @@ import java.util.Arrays; import android.animation.Keyframe.IntKeyframe; import android.animation.Keyframe.FloatKeyframe; import android.animation.Keyframe.ObjectKeyframe; +import android.graphics.Path; import android.util.Log; /** @@ -28,7 +29,7 @@ import android.util.Log; * values between those keyframes for a given animation. The class internal to the animation * package because it is an implementation detail of how Keyframes are stored and used. */ -class KeyframeSet { +class KeyframeSet implements Keyframes { int mNumKeyframes; @@ -52,7 +53,12 @@ class KeyframeSet { * If subclass has variables that it calculates based on the Keyframes, it should reset them * when this method is called because Keyframe contents might have changed. */ - void invalidateCache() { + @Override + public void invalidateCache() { + } + + public ArrayList<Keyframe> getKeyframes() { + return mKeyframes; } public static KeyframeSet ofInt(int... values) { @@ -144,6 +150,10 @@ class KeyframeSet { return new KeyframeSet(keyframes); } + public static PathKeyframes ofPath(Path path) { + return new PathKeyframes(path); + } + /** * Sets the TypeEvaluator to be used when calculating animated values. This object * is required only for KeyframeSets that are not either IntKeyframeSet or FloatKeyframeSet, @@ -157,6 +167,11 @@ class KeyframeSet { } @Override + public Class getType() { + return mFirstKeyframe.getType(); + } + + @Override public KeyframeSet clone() { ArrayList<Keyframe> keyframes = mKeyframes; int numKeyframes = mKeyframes.size(); diff --git a/core/java/android/animation/Keyframes.java b/core/java/android/animation/Keyframes.java new file mode 100644 index 0000000..6611c6c --- /dev/null +++ b/core/java/android/animation/Keyframes.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2014 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.animation; + +import java.util.ArrayList; + +/** + * This interface abstracts a collection of Keyframe objects and is called by + * ValueAnimator to calculate values between those keyframes for a given animation. + */ +interface Keyframes extends Cloneable { + + /** + * Sets the TypeEvaluator to be used when calculating animated values. This object + * is required only for Keyframes that are not either IntKeyframes or FloatKeyframes, + * both of which assume their own evaluator to speed up calculations with those primitive + * types. + * + * @param evaluator The TypeEvaluator to be used to calculate animated values. + */ + void setEvaluator(TypeEvaluator evaluator); + + /** + * @return The value type contained by the contained Keyframes. + */ + Class getType(); + + /** + * Gets the animated value, given the elapsed fraction of the animation (interpolated by the + * animation's interpolator) and the evaluator used to calculate in-between values. This + * function maps the input fraction to the appropriate keyframe interval and a fraction + * between them and returns the interpolated value. Note that the input fraction may fall + * outside the [0-1] bounds, if the animation's interpolator made that happen (e.g., a + * spring interpolation that might send the fraction past 1.0). We handle this situation by + * just using the two keyframes at the appropriate end when the value is outside those bounds. + * + * @param fraction The elapsed fraction of the animation + * @return The animated value. + */ + Object getValue(float fraction); + + /** + * If subclass has variables that it calculates based on the Keyframes, it should reset them + * when this method is called because Keyframe contents might have changed. + */ + void invalidateCache(); + + /** + * @return A list of all Keyframes contained by this. This may return null if this is + * not made up of Keyframes. + */ + ArrayList<Keyframe> getKeyframes(); + + Keyframes clone(); + + /** + * A specialization of Keyframes that has integer primitive value calculation. + */ + public interface IntKeyframes extends Keyframes { + + /** + * Works like {@link #getValue(float)}, but returning a primitive. + * @param fraction The elapsed fraction of the animation + * @return The animated value. + */ + int getIntValue(float fraction); + } + + /** + * A specialization of Keyframes that has float primitive value calculation. + */ + public interface FloatKeyframes extends Keyframes { + + /** + * Works like {@link #getValue(float)}, but returning a primitive. + * @param fraction The elapsed fraction of the animation + * @return The animated value. + */ + float getFloatValue(float fraction); + } +} diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java index 188408d..5790682 100644 --- a/core/java/android/animation/LayoutTransition.java +++ b/core/java/android/animation/LayoutTransition.java @@ -899,11 +899,15 @@ public class LayoutTransition { PropertyValuesHolder[] oldValues = valueAnim.getValues(); for (int i = 0; i < oldValues.length; ++i) { PropertyValuesHolder pvh = oldValues[i]; - KeyframeSet keyframeSet = pvh.mKeyframeSet; - if (keyframeSet.mFirstKeyframe == null || - keyframeSet.mLastKeyframe == null || - !keyframeSet.mFirstKeyframe.getValue().equals( - keyframeSet.mLastKeyframe.getValue())) { + if (pvh.mKeyframes instanceof KeyframeSet) { + KeyframeSet keyframeSet = (KeyframeSet) pvh.mKeyframes; + if (keyframeSet.mFirstKeyframe == null || + keyframeSet.mLastKeyframe == null || + !keyframeSet.mFirstKeyframe.getValue().equals( + keyframeSet.mLastKeyframe.getValue())) { + valuesDiffer = true; + } + } else if (!pvh.mKeyframes.getValue(0).equals(pvh.mKeyframes.getValue(1))) { valuesDiffer = true; } } diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java index a4ac73f..500634c 100644 --- a/core/java/android/animation/ObjectAnimator.java +++ b/core/java/android/animation/ObjectAnimator.java @@ -239,9 +239,11 @@ public final class ObjectAnimator extends ValueAnimator { */ public static ObjectAnimator ofInt(Object target, String xPropertyName, String yPropertyName, Path path) { - Keyframe[][] keyframes = PropertyValuesHolder.createKeyframes(path, true); - PropertyValuesHolder x = PropertyValuesHolder.ofKeyframe(xPropertyName, keyframes[0]); - PropertyValuesHolder y = PropertyValuesHolder.ofKeyframe(yPropertyName, keyframes[1]); + PathKeyframes keyframes = KeyframeSet.ofPath(path); + PropertyValuesHolder x = PropertyValuesHolder.ofKeyframes(xPropertyName, + keyframes.createXIntKeyframes()); + PropertyValuesHolder y = PropertyValuesHolder.ofKeyframes(yPropertyName, + keyframes.createYIntKeyframes()); return ofPropertyValuesHolder(target, x, y); } @@ -278,9 +280,11 @@ public final class ObjectAnimator extends ValueAnimator { */ public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> xProperty, Property<T, Integer> yProperty, Path path) { - Keyframe[][] keyframes = PropertyValuesHolder.createKeyframes(path, true); - PropertyValuesHolder x = PropertyValuesHolder.ofKeyframe(xProperty, keyframes[0]); - PropertyValuesHolder y = PropertyValuesHolder.ofKeyframe(yProperty, keyframes[1]); + PathKeyframes keyframes = KeyframeSet.ofPath(path); + PropertyValuesHolder x = PropertyValuesHolder.ofKeyframes(xProperty, + keyframes.createXIntKeyframes()); + PropertyValuesHolder y = PropertyValuesHolder.ofKeyframes(yProperty, + keyframes.createYIntKeyframes()); return ofPropertyValuesHolder(target, x, y); } @@ -429,9 +433,11 @@ public final class ObjectAnimator extends ValueAnimator { */ public static ObjectAnimator ofFloat(Object target, String xPropertyName, String yPropertyName, Path path) { - Keyframe[][] keyframes = PropertyValuesHolder.createKeyframes(path, false); - PropertyValuesHolder x = PropertyValuesHolder.ofKeyframe(xPropertyName, keyframes[0]); - PropertyValuesHolder y = PropertyValuesHolder.ofKeyframe(yPropertyName, keyframes[1]); + PathKeyframes keyframes = KeyframeSet.ofPath(path); + PropertyValuesHolder x = PropertyValuesHolder.ofKeyframes(xPropertyName, + keyframes.createXFloatKeyframes()); + PropertyValuesHolder y = PropertyValuesHolder.ofKeyframes(yPropertyName, + keyframes.createYFloatKeyframes()); return ofPropertyValuesHolder(target, x, y); } @@ -469,9 +475,11 @@ public final class ObjectAnimator extends ValueAnimator { */ public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> xProperty, Property<T, Float> yProperty, Path path) { - Keyframe[][] keyframes = PropertyValuesHolder.createKeyframes(path, false); - PropertyValuesHolder x = PropertyValuesHolder.ofKeyframe(xProperty, keyframes[0]); - PropertyValuesHolder y = PropertyValuesHolder.ofKeyframe(yProperty, keyframes[1]); + PathKeyframes keyframes = KeyframeSet.ofPath(path); + PropertyValuesHolder x = PropertyValuesHolder.ofKeyframes(xProperty, + keyframes.createXFloatKeyframes()); + PropertyValuesHolder y = PropertyValuesHolder.ofKeyframes(yProperty, + keyframes.createYFloatKeyframes()); return ofPropertyValuesHolder(target, x, y); } @@ -652,6 +660,10 @@ public final class ObjectAnimator extends ValueAnimator { * uses a type other than <code>PointF</code>, <code>converter</code> can be used to change * from <code>PointF</code> to the type associated with the <code>Property</code>. * + * <p>The PointF passed to <code>converter</code> or <code>property</code>, if + * <code>converter</code> is <code>null</code>, is reused on each animation frame and should + * not be stored by the setter or TypeConverter.</p> + * * @param target The object whose property is to be animated. * @param property The property being animated. Should not be null. * @param converter Converts a PointF to the type associated with the setter. May be @@ -809,10 +821,9 @@ public final class ObjectAnimator extends ValueAnimator { Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration()); for (int i = 0; i < mValues.length; ++i) { PropertyValuesHolder pvh = mValues[i]; - ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes; Log.d(LOG_TAG, " Values[" + i + "]: " + - pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " + - keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue()); + pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " + + pvh.mKeyframes.getValue(1)); } } super.start(); diff --git a/core/java/android/animation/PathKeyframes.java b/core/java/android/animation/PathKeyframes.java new file mode 100644 index 0000000..70eed90 --- /dev/null +++ b/core/java/android/animation/PathKeyframes.java @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2014 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.animation; + +import android.graphics.Path; +import android.graphics.PointF; +import android.util.MathUtils; + +import java.util.ArrayList; + +/** + * PathKeyframes relies on approximating the Path as a series of line segments. + * The line segments are recursively divided until there is less than 1/2 pixel error + * between the lines and the curve. Each point of the line segment is converted + * to a Keyframe and a linear interpolation between Keyframes creates a good approximation + * of the curve. + * <p> + * PathKeyframes is optimized to reduce the number of objects created when there are + * many keyframes for a curve. + * </p> + * <p> + * Typically, the returned type is a PointF, but the individual components can be extracted + * as either an IntKeyframes or FloatKeyframes. + * </p> + */ +class PathKeyframes implements Keyframes { + private static final int FRACTION_OFFSET = 0; + private static final int X_OFFSET = 1; + private static final int Y_OFFSET = 2; + private static final int NUM_COMPONENTS = 3; + private static final ArrayList<Keyframe> EMPTY_KEYFRAMES = new ArrayList<Keyframe>(); + + private PointF mTempPointF = new PointF(); + private float[] mKeyframeData; + + public PathKeyframes(Path path) { + this(path, 0.5f); + } + + public PathKeyframes(Path path, float error) { + if (path == null || path.isEmpty()) { + throw new IllegalArgumentException("The path must not be null or empty"); + } + mKeyframeData = path.approximate(error); + } + + @Override + public ArrayList<Keyframe> getKeyframes() { + return EMPTY_KEYFRAMES; + } + + @Override + public Object getValue(float fraction) { + fraction = MathUtils.constrain(fraction, 0, 1); + + int numPoints = mKeyframeData.length / 3; + + if (fraction == 0) { + return pointForIndex(0); + } else if (fraction == 1) { + return pointForIndex(numPoints - 1); + } else { + // Binary search for the correct section + int low = 0; + int high = numPoints - 1; + + while (low <= high) { + int mid = (low + high) / 2; + float midFraction = mKeyframeData[(mid * NUM_COMPONENTS) + FRACTION_OFFSET]; + + if (fraction < midFraction) { + high = mid - 1; + } else if (fraction > midFraction) { + low = mid + 1; + } else { + return pointForIndex(mid); + } + } + + // now high is below the fraction and low is above the fraction + int startBase = (high * NUM_COMPONENTS); + int endBase = (low * NUM_COMPONENTS); + + float startFraction = mKeyframeData[startBase + FRACTION_OFFSET]; + float endFraction = mKeyframeData[endBase + FRACTION_OFFSET]; + + float intervalFraction = (fraction - startFraction)/(endFraction - startFraction); + + float startX = mKeyframeData[startBase + X_OFFSET]; + float endX = mKeyframeData[endBase + X_OFFSET]; + float startY = mKeyframeData[startBase + Y_OFFSET]; + float endY = mKeyframeData[endBase + Y_OFFSET]; + + float x = interpolate(intervalFraction, startX, endX); + float y = interpolate(intervalFraction, startY, endY); + + mTempPointF.set(x, y); + return mTempPointF; + } + } + + @Override + public void invalidateCache() { + } + + @Override + public void setEvaluator(TypeEvaluator evaluator) { + } + + @Override + public Class getType() { + return PointF.class; + } + + @Override + public Keyframes clone() { + Keyframes clone = null; + try { + clone = (Keyframes) super.clone(); + } catch (CloneNotSupportedException e) {} + return clone; + } + + private PointF pointForIndex(int index) { + int base = (index * NUM_COMPONENTS); + int xOffset = base + X_OFFSET; + int yOffset = base + Y_OFFSET; + mTempPointF.set(mKeyframeData[xOffset], mKeyframeData[yOffset]); + return mTempPointF; + } + + private static float interpolate(float fraction, float startValue, float endValue) { + float diff = endValue - startValue; + return startValue + (diff * fraction); + } + + /** + * Returns a FloatKeyframes for the X component of the Path. + * @return a FloatKeyframes for the X component of the Path. + */ + public FloatKeyframes createXFloatKeyframes() { + return new FloatKeyframesBase() { + @Override + public float getFloatValue(float fraction) { + PointF pointF = (PointF) PathKeyframes.this.getValue(fraction); + return pointF.x; + } + }; + } + + /** + * Returns a FloatKeyframes for the Y component of the Path. + * @return a FloatKeyframes for the Y component of the Path. + */ + public FloatKeyframes createYFloatKeyframes() { + return new FloatKeyframesBase() { + @Override + public float getFloatValue(float fraction) { + PointF pointF = (PointF) PathKeyframes.this.getValue(fraction); + return pointF.y; + } + }; + } + + /** + * Returns an IntKeyframes for the X component of the Path. + * @return an IntKeyframes for the X component of the Path. + */ + public IntKeyframes createXIntKeyframes() { + return new IntKeyframesBase() { + @Override + public int getIntValue(float fraction) { + PointF pointF = (PointF) PathKeyframes.this.getValue(fraction); + return Math.round(pointF.x); + } + }; + } + + /** + * Returns an IntKeyframeSet for the Y component of the Path. + * @return an IntKeyframeSet for the Y component of the Path. + */ + public IntKeyframes createYIntKeyframes() { + return new IntKeyframesBase() { + @Override + public int getIntValue(float fraction) { + PointF pointF = (PointF) PathKeyframes.this.getValue(fraction); + return Math.round(pointF.y); + } + }; + } + + private abstract static class SimpleKeyframes implements Keyframes { + @Override + public void setEvaluator(TypeEvaluator evaluator) { + } + + @Override + public void invalidateCache() { + } + + @Override + public ArrayList<Keyframe> getKeyframes() { + return EMPTY_KEYFRAMES; + } + + @Override + public Keyframes clone() { + Keyframes clone = null; + try { + clone = (Keyframes) super.clone(); + } catch (CloneNotSupportedException e) {} + return clone; + } + } + + private abstract static class IntKeyframesBase extends SimpleKeyframes implements IntKeyframes { + @Override + public Class getType() { + return Integer.class; + } + + @Override + public Object getValue(float fraction) { + return getIntValue(fraction); + } + } + + private abstract static class FloatKeyframesBase extends SimpleKeyframes + implements FloatKeyframes { + @Override + public Class getType() { + return Float.class; + } + + @Override + public Object getValue(float fraction) { + return getFloatValue(fraction); + } + } +} diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java index 73b83ef..d372933 100644 --- a/core/java/android/animation/PropertyValuesHolder.java +++ b/core/java/android/animation/PropertyValuesHolder.java @@ -18,7 +18,6 @@ package android.animation; import android.graphics.Path; import android.graphics.PointF; -import android.util.FloatMath; import android.util.FloatProperty; import android.util.IntProperty; import android.util.Log; @@ -26,6 +25,7 @@ import android.util.Property; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.HashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -75,7 +75,7 @@ public class PropertyValuesHolder implements Cloneable { /** * The set of keyframes (time/value pairs) that define this animation. */ - KeyframeSet mKeyframeSet = null; + Keyframes mKeyframes = null; // type evaluators for the primitive types handled by this implementation @@ -219,11 +219,9 @@ public class PropertyValuesHolder implements Cloneable { * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...) */ public static PropertyValuesHolder ofMultiInt(String propertyName, Path path) { - Keyframe[] keyframes = createKeyframes(path); - KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(keyframes); - TypeEvaluator<PointF> evaluator = new PointFEvaluator(new PointF()); + Keyframes keyframes = KeyframeSet.ofPath(path); PointFToIntArray converter = new PointFToIntArray(); - return new MultiIntValuesHolder(propertyName, converter, evaluator, keyframeSet); + return new MultiIntValuesHolder(propertyName, converter, null, keyframes); } /** @@ -339,11 +337,9 @@ public class PropertyValuesHolder implements Cloneable { * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...) */ public static PropertyValuesHolder ofMultiFloat(String propertyName, Path path) { - Keyframe[] keyframes = createKeyframes(path); - KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(keyframes); - TypeEvaluator<PointF> evaluator = new PointFEvaluator(new PointF()); + Keyframes keyframes = KeyframeSet.ofPath(path); PointFToFloatArray converter = new PointFToFloatArray(); - return new MultiFloatValuesHolder(propertyName, converter, evaluator, keyframeSet); + return new MultiFloatValuesHolder(propertyName, converter, null, keyframes); } /** @@ -415,6 +411,10 @@ public class PropertyValuesHolder implements Cloneable { * <code>TypeConverter</code> to convert from <code>PointF</code> to the target * type. * + * <p>The PointF passed to <code>converter</code> or <code>property</code>, if + * <code>converter</code> is <code>null</code>, is reused on each animation frame and should + * not be stored by the setter or TypeConverter.</p> + * * @param propertyName The name of the property being animated. * @param converter Converts a PointF to the type associated with the setter. May be * null if conversion is unnecessary. @@ -423,9 +423,9 @@ public class PropertyValuesHolder implements Cloneable { */ public static PropertyValuesHolder ofObject(String propertyName, TypeConverter<PointF, ?> converter, Path path) { - Keyframe[] keyframes = createKeyframes(path); - PropertyValuesHolder pvh = ofKeyframe(propertyName, keyframes); - pvh.setEvaluator(new PointFEvaluator(new PointF())); + PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); + pvh.mKeyframes = KeyframeSet.ofPath(path); + pvh.mValueType = PointF.class; pvh.setConverter(converter); return pvh; } @@ -484,6 +484,10 @@ public class PropertyValuesHolder implements Cloneable { * <code>TypeConverter</code> to convert from <code>PointF</code> to the target * type. * + * <p>The PointF passed to <code>converter</code> or <code>property</code>, if + * <code>converter</code> is <code>null</code>, is reused on each animation frame and should + * not be stored by the setter or TypeConverter.</p> + * * @param property The property being animated. Should not be null. * @param converter Converts a PointF to the type associated with the setter. May be * null if conversion is unnecessary. @@ -492,9 +496,9 @@ public class PropertyValuesHolder implements Cloneable { */ public static <V> PropertyValuesHolder ofObject(Property<?, V> property, TypeConverter<PointF, V> converter, Path path) { - Keyframe[] keyframes = createKeyframes(path); - PropertyValuesHolder pvh = ofKeyframe(property, keyframes); - pvh.setEvaluator(new PointFEvaluator(new PointF())); + PropertyValuesHolder pvh = new PropertyValuesHolder(property); + pvh.mKeyframes = KeyframeSet.ofPath(path); + pvh.mValueType = PointF.class; pvh.setConverter(converter); return pvh; } @@ -520,17 +524,7 @@ public class PropertyValuesHolder implements Cloneable { */ public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) { KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); - if (keyframeSet instanceof IntKeyframeSet) { - return new IntPropertyValuesHolder(propertyName, (IntKeyframeSet) keyframeSet); - } else if (keyframeSet instanceof FloatKeyframeSet) { - return new FloatPropertyValuesHolder(propertyName, (FloatKeyframeSet) keyframeSet); - } - else { - PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); - pvh.mKeyframeSet = keyframeSet; - pvh.mValueType = ((Keyframe)values[0]).getType(); - return pvh; - } + return ofKeyframes(propertyName, keyframeSet); } /** @@ -551,15 +545,32 @@ public class PropertyValuesHolder implements Cloneable { */ public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) { KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); - if (keyframeSet instanceof IntKeyframeSet) { - return new IntPropertyValuesHolder(property, (IntKeyframeSet) keyframeSet); - } else if (keyframeSet instanceof FloatKeyframeSet) { - return new FloatPropertyValuesHolder(property, (FloatKeyframeSet) keyframeSet); + return ofKeyframes(property, keyframeSet); + } + + static PropertyValuesHolder ofKeyframes(String propertyName, Keyframes keyframes) { + if (keyframes instanceof Keyframes.IntKeyframes) { + return new IntPropertyValuesHolder(propertyName, (Keyframes.IntKeyframes) keyframes); + } else if (keyframes instanceof Keyframes.FloatKeyframes) { + return new FloatPropertyValuesHolder(propertyName, + (Keyframes.FloatKeyframes) keyframes); + } else { + PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName); + pvh.mKeyframes = keyframes; + pvh.mValueType = keyframes.getType(); + return pvh; } - else { + } + + static PropertyValuesHolder ofKeyframes(Property property, Keyframes keyframes) { + if (keyframes instanceof Keyframes.IntKeyframes) { + return new IntPropertyValuesHolder(property, (Keyframes.IntKeyframes) keyframes); + } else if (keyframes instanceof Keyframes.FloatKeyframes) { + return new FloatPropertyValuesHolder(property, (Keyframes.FloatKeyframes) keyframes); + } else { PropertyValuesHolder pvh = new PropertyValuesHolder(property); - pvh.mKeyframeSet = keyframeSet; - pvh.mValueType = ((Keyframe)values[0]).getType(); + pvh.mKeyframes = keyframes; + pvh.mValueType = keyframes.getType(); return pvh; } } @@ -579,7 +590,7 @@ public class PropertyValuesHolder implements Cloneable { */ public void setIntValues(int... values) { mValueType = int.class; - mKeyframeSet = KeyframeSet.ofInt(values); + mKeyframes = KeyframeSet.ofInt(values); } /** @@ -597,7 +608,7 @@ public class PropertyValuesHolder implements Cloneable { */ public void setFloatValues(float... values) { mValueType = float.class; - mKeyframeSet = KeyframeSet.ofFloat(values); + mKeyframes = KeyframeSet.ofFloat(values); } /** @@ -612,7 +623,7 @@ public class PropertyValuesHolder implements Cloneable { for (int i = 0; i < numKeyframes; ++i) { keyframes[i] = (Keyframe)values[i]; } - mKeyframeSet = new KeyframeSet(keyframes); + mKeyframes = new KeyframeSet(keyframes); } /** @@ -630,9 +641,9 @@ public class PropertyValuesHolder implements Cloneable { */ public void setObjectValues(Object... values) { mValueType = values[0].getClass(); - mKeyframeSet = KeyframeSet.ofObject(values); + mKeyframes = KeyframeSet.ofObject(values); if (mEvaluator != null) { - mKeyframeSet.setEvaluator(mEvaluator); + mKeyframes.setEvaluator(mEvaluator); } } @@ -775,12 +786,15 @@ public class PropertyValuesHolder implements Cloneable { * @param target The object on which the setter (and possibly getter) exist. */ void setupSetterAndGetter(Object target) { - mKeyframeSet.invalidateCache(); + mKeyframes.invalidateCache(); if (mProperty != null) { // check to make sure that mProperty is on the class of target try { Object testValue = null; - for (Keyframe kf : mKeyframeSet.mKeyframes) { + ArrayList<Keyframe> keyframes = mKeyframes.getKeyframes(); + int keyframeCount = keyframes == null ? 0 : keyframes.size(); + for (int i = 0; i < keyframeCount; i++) { + Keyframe kf = keyframes.get(i); if (!kf.hasValue() || kf.valueWasSetOnStart()) { if (testValue == null) { testValue = convertBack(mProperty.get(target)); @@ -800,7 +814,10 @@ public class PropertyValuesHolder implements Cloneable { if (mSetter == null) { setupSetter(targetClass); } - for (Keyframe kf : mKeyframeSet.mKeyframes) { + ArrayList<Keyframe> keyframes = mKeyframes.getKeyframes(); + int keyframeCount = keyframes == null ? 0 : keyframes.size(); + for (int i = 0; i < keyframeCount; i++) { + Keyframe kf = keyframes.get(i); if (!kf.hasValue() || kf.valueWasSetOnStart()) { if (mGetter == null) { setupGetter(targetClass); @@ -873,7 +890,10 @@ public class PropertyValuesHolder implements Cloneable { * @param target The object which holds the start values that should be set. */ void setupStartValue(Object target) { - setupValue(target, mKeyframeSet.mKeyframes.get(0)); + ArrayList<Keyframe> keyframes = mKeyframes.getKeyframes(); + if (!keyframes.isEmpty()) { + setupValue(target, keyframes.get(0)); + } } /** @@ -885,7 +905,10 @@ public class PropertyValuesHolder implements Cloneable { * @param target The object which holds the start values that should be set. */ void setupEndValue(Object target) { - setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1)); + ArrayList<Keyframe> keyframes = mKeyframes.getKeyframes(); + if (!keyframes.isEmpty()) { + setupValue(target, keyframes.get(keyframes.size() - 1)); + } } @Override @@ -894,7 +917,7 @@ public class PropertyValuesHolder implements Cloneable { PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone(); newPVH.mPropertyName = mPropertyName; newPVH.mProperty = mProperty; - newPVH.mKeyframeSet = mKeyframeSet.clone(); + newPVH.mKeyframes = mKeyframes.clone(); newPVH.mEvaluator = mEvaluator; return newPVH; } catch (CloneNotSupportedException e) { @@ -941,7 +964,7 @@ public class PropertyValuesHolder implements Cloneable { if (mEvaluator != null) { // KeyframeSet knows how to evaluate the common types - only give it a custom // evaluator if one has been set on this class - mKeyframeSet.setEvaluator(mEvaluator); + mKeyframes.setEvaluator(mEvaluator); } } @@ -957,7 +980,7 @@ public class PropertyValuesHolder implements Cloneable { */ public void setEvaluator(TypeEvaluator evaluator) { mEvaluator = evaluator; - mKeyframeSet.setEvaluator(evaluator); + mKeyframes.setEvaluator(evaluator); } /** @@ -967,7 +990,7 @@ public class PropertyValuesHolder implements Cloneable { * @param fraction The elapsed, interpolated fraction of the animation. */ void calculateValue(float fraction) { - Object value = mKeyframeSet.getValue(fraction); + Object value = mKeyframes.getValue(fraction); mAnimatedValue = mConverter == null ? value : mConverter.convert(value); } @@ -1025,7 +1048,7 @@ public class PropertyValuesHolder implements Cloneable { @Override public String toString() { - return mPropertyName + ": " + mKeyframeSet.toString(); + return mPropertyName + ": " + mKeyframes.toString(); } /** @@ -1059,21 +1082,21 @@ public class PropertyValuesHolder implements Cloneable { long mJniSetter; private IntProperty mIntProperty; - IntKeyframeSet mIntKeyframeSet; + Keyframes.IntKeyframes mIntKeyframes; int mIntAnimatedValue; - public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) { + public IntPropertyValuesHolder(String propertyName, Keyframes.IntKeyframes keyframes) { super(propertyName); mValueType = int.class; - mKeyframeSet = keyframeSet; - mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet; + mKeyframes = keyframes; + mIntKeyframes = keyframes; } - public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) { + public IntPropertyValuesHolder(Property property, Keyframes.IntKeyframes keyframes) { super(property); mValueType = int.class; - mKeyframeSet = keyframeSet; - mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet; + mKeyframes = keyframes; + mIntKeyframes = keyframes; if (property instanceof IntProperty) { mIntProperty = (IntProperty) mProperty; } @@ -1095,12 +1118,12 @@ public class PropertyValuesHolder implements Cloneable { @Override public void setIntValues(int... values) { super.setIntValues(values); - mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet; + mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes; } @Override void calculateValue(float fraction) { - mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction); + mIntAnimatedValue = mIntKeyframes.getIntValue(fraction); } @Override @@ -1111,7 +1134,7 @@ public class PropertyValuesHolder implements Cloneable { @Override public IntPropertyValuesHolder clone() { IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone(); - newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet; + newPVH.mIntKeyframes = (Keyframes.IntKeyframes) newPVH.mKeyframes; return newPVH; } @@ -1196,21 +1219,21 @@ public class PropertyValuesHolder implements Cloneable { long mJniSetter; private FloatProperty mFloatProperty; - FloatKeyframeSet mFloatKeyframeSet; + Keyframes.FloatKeyframes mFloatKeyframes; float mFloatAnimatedValue; - public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) { + public FloatPropertyValuesHolder(String propertyName, Keyframes.FloatKeyframes keyframes) { super(propertyName); mValueType = float.class; - mKeyframeSet = keyframeSet; - mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet; + mKeyframes = keyframes; + mFloatKeyframes = keyframes; } - public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) { + public FloatPropertyValuesHolder(Property property, Keyframes.FloatKeyframes keyframes) { super(property); mValueType = float.class; - mKeyframeSet = keyframeSet; - mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet; + mKeyframes = keyframes; + mFloatKeyframes = keyframes; if (property instanceof FloatProperty) { mFloatProperty = (FloatProperty) mProperty; } @@ -1232,12 +1255,12 @@ public class PropertyValuesHolder implements Cloneable { @Override public void setFloatValues(float... values) { super.setFloatValues(values); - mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet; + mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes; } @Override void calculateValue(float fraction) { - mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction); + mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction); } @Override @@ -1248,7 +1271,7 @@ public class PropertyValuesHolder implements Cloneable { @Override public FloatPropertyValuesHolder clone() { FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone(); - newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet; + newPVH.mFloatKeyframes = (Keyframes.FloatKeyframes) newPVH.mKeyframes; return newPVH; } @@ -1340,10 +1363,10 @@ public class PropertyValuesHolder implements Cloneable { } public MultiFloatValuesHolder(String propertyName, TypeConverter converter, - TypeEvaluator evaluator, KeyframeSet keyframeSet) { + TypeEvaluator evaluator, Keyframes keyframes) { super(propertyName); setConverter(converter); - mKeyframeSet = keyframeSet; + mKeyframes = keyframes; setEvaluator(evaluator); } @@ -1443,10 +1466,10 @@ public class PropertyValuesHolder implements Cloneable { } public MultiIntValuesHolder(String propertyName, TypeConverter converter, - TypeEvaluator evaluator, KeyframeSet keyframeSet) { + TypeEvaluator evaluator, Keyframes keyframes) { super(propertyName); setConverter(converter); - mKeyframeSet = keyframeSet; + mKeyframes = keyframes; setEvaluator(evaluator); } @@ -1532,77 +1555,6 @@ public class PropertyValuesHolder implements Cloneable { } } - /* Path interpolation relies on approximating the Path as a series of line segments. - The line segments are recursively divided until there is less than 1/2 pixel error - between the lines and the curve. Each point of the line segment is converted - to a Keyframe and a linear interpolation between Keyframes creates a good approximation - of the curve. - - The fraction for each Keyframe is the length along the Path to the point, divided by - the total Path length. Two points may have the same fraction in the case of a move - command causing a disjoint Path. - - The value for each Keyframe is either the point as a PointF or one of the x or y - coordinates as an int or float. In the latter case, two Keyframes are generated for - each point that have the same fraction. */ - - /** - * Returns separate Keyframes arrays for the x and y coordinates along a Path. If - * isInt is true, the Keyframes will be IntKeyframes, otherwise they will be FloatKeyframes. - * The element at index 0 are the x coordinate Keyframes and element at index 1 are the - * y coordinate Keyframes. The returned values can be linearly interpolated and get less - * than 1/2 pixel error. - */ - static Keyframe[][] createKeyframes(Path path, boolean isInt) { - if (path == null || path.isEmpty()) { - throw new IllegalArgumentException("The path must not be null or empty"); - } - float[] pointComponents = path.approximate(0.5f); - - int numPoints = pointComponents.length / 3; - - Keyframe[][] keyframes = new Keyframe[2][]; - keyframes[0] = new Keyframe[numPoints]; - keyframes[1] = new Keyframe[numPoints]; - int componentIndex = 0; - for (int i = 0; i < numPoints; i++) { - float fraction = pointComponents[componentIndex++]; - float x = pointComponents[componentIndex++]; - float y = pointComponents[componentIndex++]; - if (isInt) { - keyframes[0][i] = Keyframe.ofInt(fraction, Math.round(x)); - keyframes[1][i] = Keyframe.ofInt(fraction, Math.round(y)); - } else { - keyframes[0][i] = Keyframe.ofFloat(fraction, x); - keyframes[1][i] = Keyframe.ofFloat(fraction, y); - } - } - return keyframes; - } - - /** - * Returns PointF Keyframes for a Path. The resulting points can be linearly interpolated - * with less than 1/2 pixel in error. - */ - private static Keyframe[] createKeyframes(Path path) { - if (path == null || path.isEmpty()) { - throw new IllegalArgumentException("The path must not be null or empty"); - } - float[] pointComponents = path.approximate(0.5f); - - int numPoints = pointComponents.length / 3; - - Keyframe[] keyframes = new Keyframe[numPoints]; - int componentIndex = 0; - for (int i = 0; i < numPoints; i++) { - float fraction = pointComponents[componentIndex++]; - float x = pointComponents[componentIndex++]; - float y = pointComponents[componentIndex++]; - keyframes[i] = Keyframe.ofObject(fraction, new PointF(x, y)); - } - return keyframes; - } - /** * Convert from PointF to float[] for multi-float setters along a Path. */ diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 2503d17..8ab344e 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1236,18 +1236,22 @@ public class Activity extends ContextThemeWrapper } /** + * @hide * Check whether this activity is running as part of a voice interaction with the user. * If true, it should perform its interaction with the user through the * {@link VoiceInteractor} returned by {@link #getVoiceInteractor}. */ + @SystemApi public boolean isVoiceInteraction() { return mVoiceInteractor != null; } /** + * @hide * Retrieve the active {@link VoiceInteractor} that the user is going through to * interact with this activity. */ + @SystemApi public VoiceInteractor getVoiceInteractor() { return mVoiceInteractor; } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 66928ca..ba9c9d6 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -220,6 +220,9 @@ public class AppOpsManager { /** Continually monitoring location data with a relatively high power request. */ public static final String OPSTR_MONITOR_HIGH_POWER_LOCATION = "android:monitor_location_high_power"; + /** Access to {@link android.app.usage.UsageStatsManager}. */ + public static final String OPSTR_GET_USAGE_STATS + = "android:get_usage_stats"; /** Activate a VPN connection without user intervention. @hide */ @SystemApi public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn"; @@ -331,7 +334,7 @@ public class AppOpsManager { null, OPSTR_MONITOR_LOCATION, OPSTR_MONITOR_HIGH_POWER_LOCATION, - null, + OPSTR_GET_USAGE_STATS, null, null, null, diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 9342ae5..6843827 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1494,57 +1494,52 @@ final class ApplicationPackageManager extends PackageManager { return false; } + /** @hide */ @Override public KeySet getKeySetByAlias(String packageName, String alias) { Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(alias); - IBinder keySetToken; + KeySet ks; try { - keySetToken = mPM.getKeySetByAlias(packageName, alias); + ks = mPM.getKeySetByAlias(packageName, alias); } catch (RemoteException e) { return null; } - if (keySetToken == null) { - return null; - } - return new KeySet(keySetToken); + return ks; } + /** @hide */ @Override public KeySet getSigningKeySet(String packageName) { Preconditions.checkNotNull(packageName); - IBinder keySetToken; + KeySet ks; try { - keySetToken = mPM.getSigningKeySet(packageName); + ks = mPM.getSigningKeySet(packageName); } catch (RemoteException e) { return null; } - if (keySetToken == null) { - return null; - } - return new KeySet(keySetToken); + return ks; } - + /** @hide */ @Override public boolean isSignedBy(String packageName, KeySet ks) { Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(ks); - IBinder keySetToken = ks.getToken(); try { - return mPM.isPackageSignedByKeySet(packageName, keySetToken); + return mPM.isPackageSignedByKeySet(packageName, ks); } catch (RemoteException e) { return false; } } + /** @hide */ @Override public boolean isSignedByExactly(String packageName, KeySet ks) { Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(ks); - IBinder keySetToken = ks.getToken(); try { - return mPM.isPackageSignedByKeySetExactly(packageName, keySetToken); + return mPM.isPackageSignedByKeySetExactly(packageName, ks); } catch (RemoteException e) { return false; } diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java index 67863a5..59f010c 100644 --- a/core/java/android/app/BackStackRecord.java +++ b/core/java/android/app/BackStackRecord.java @@ -23,15 +23,17 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.transition.Transition; -import android.transition.TransitionInflater; import android.transition.TransitionManager; import android.transition.TransitionSet; +import android.transition.TransitionUtils; import android.util.ArrayMap; import android.util.Log; import android.util.LogWriter; import android.util.Pair; +import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -42,8 +44,6 @@ final class BackStackState implements Parcelable { final int[] mOps; final int mTransition; final int mTransitionStyle; - final int mCustomTransition; - final int mSceneRoot; final String mName; final int mIndex; final int mBreadCrumbTitleRes; @@ -96,8 +96,6 @@ final class BackStackState implements Parcelable { mBreadCrumbTitleText = bse.mBreadCrumbTitleText; mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes; mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText; - mCustomTransition = bse.mCustomTransition; - mSceneRoot = bse.mSceneRoot; mSharedElementSourceNames = bse.mSharedElementSourceNames; mSharedElementTargetNames = bse.mSharedElementTargetNames; } @@ -112,8 +110,6 @@ final class BackStackState implements Parcelable { mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mBreadCrumbShortTitleRes = in.readInt(); mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - mCustomTransition = in.readInt(); - mSceneRoot = in.readInt(); mSharedElementSourceNames = in.createStringArrayList(); mSharedElementTargetNames = in.createStringArrayList(); } @@ -164,8 +160,6 @@ final class BackStackState implements Parcelable { bse.mBreadCrumbTitleText = mBreadCrumbTitleText; bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes; bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText; - bse.mCustomTransition = mCustomTransition; - bse.mSceneRoot = mSceneRoot; bse.mSharedElementSourceNames = mSharedElementSourceNames; bse.mSharedElementTargetNames = mSharedElementTargetNames; bse.bumpBackStackNesting(1); @@ -186,8 +180,6 @@ final class BackStackState implements Parcelable { TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0); dest.writeInt(mBreadCrumbShortTitleRes); TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0); - dest.writeInt(mCustomTransition); - dest.writeInt(mSceneRoot); dest.writeStringList(mSharedElementSourceNames); dest.writeStringList(mSharedElementTargetNames); } @@ -254,8 +246,6 @@ final class BackStackRecord extends FragmentTransaction implements int mBreadCrumbShortTitleRes; CharSequence mBreadCrumbShortTitleText; - int mCustomTransition; - int mSceneRoot; ArrayList<String> mSharedElementSourceNames; ArrayList<String> mSharedElementTargetNames; @@ -573,13 +563,6 @@ final class BackStackRecord extends FragmentTransaction implements } @Override - public FragmentTransaction setCustomTransition(int sceneRootId, int transitionId) { - mSceneRoot = sceneRootId; - mCustomTransition = transitionId; - return this; - } - - @Override public FragmentTransaction addSharedElement(View sharedElement, String name) { String transitionName = sharedElement.getTransitionName(); if (transitionName == null) { @@ -760,8 +743,15 @@ final class BackStackRecord extends FragmentTransaction implements bumpBackStackNesting(1); - TransitionState state = beginTransition(mSharedElementSourceNames, - mSharedElementTargetNames); + SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>(); + SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>(); + + calculateFragments(firstOutFragments, lastInFragments); + + TransitionState state = null; + if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) { + state = beginTransition(firstOutFragments, lastInFragments, false); + } Op op = mHead; while (op != null) { @@ -854,144 +844,608 @@ final class BackStackRecord extends FragmentTransaction implements } if (state != null) { - updateTransitionEndState(state, mSharedElementTargetNames); + updateTransitionEndState(state, firstOutFragments, lastInFragments, false); } } - private TransitionState beginTransition(ArrayList<String> sourceNames, - ArrayList<String> targetNames) { - if (mCustomTransition <= 0 || mSceneRoot <= 0) { - return null; + private static void setFirstOut(SparseArray<Fragment> fragments, Fragment fragment) { + if (fragment != null) { + int containerId = fragment.mContainerId; + if (containerId != 0 && !fragment.isHidden() && fragment.isAdded() && + fragment.getView() != null && fragments.get(containerId) == null) { + fragments.put(containerId, fragment); + } + } + } + + private void setLastIn(SparseArray<Fragment> fragments, Fragment fragment) { + if (fragment != null) { + int containerId = fragment.mContainerId; + if (containerId != 0) { + fragments.put(containerId, fragment); + } + } + } + + /** + * Finds the first removed fragment and last added fragments when going forward. + * If none of the fragments have transitions, then both lists will be empty. + * + * @param firstOutFragments The list of first fragments to be removed, keyed on the + * container ID. This list will be modified by the method. + * @param lastInFragments The list of last fragments to be added, keyed on the + * container ID. This list will be modified by the method. + */ + private void calculateFragments(SparseArray<Fragment> firstOutFragments, + SparseArray<Fragment> lastInFragments) { + Op op = mHead; + while (op != null) { + switch (op.cmd) { + case OP_ADD: + setLastIn(lastInFragments, op.fragment); + break; + case OP_REPLACE: { + Fragment f = op.fragment; + if (mManager.mAdded != null) { + for (int i = 0; i < mManager.mAdded.size(); i++) { + Fragment old = mManager.mAdded.get(i); + if (f == null || old.mContainerId == f.mContainerId) { + if (old == f) { + f = null; + } else { + setFirstOut(firstOutFragments, old); + } + } + } + } + setLastIn(lastInFragments, f); + break; + } + case OP_REMOVE: + setFirstOut(firstOutFragments, op.fragment); + break; + case OP_HIDE: + setFirstOut(firstOutFragments, op.fragment); + break; + case OP_SHOW: + setLastIn(lastInFragments, op.fragment); + break; + case OP_DETACH: + setFirstOut(firstOutFragments, op.fragment); + break; + case OP_ATTACH: + setLastIn(lastInFragments, op.fragment); + break; + } + + op = op.next; + } + + if (!haveTransitions(firstOutFragments, lastInFragments, false)) { + firstOutFragments.clear(); + lastInFragments.clear(); + } + } + + /** + * @return true if custom transitions exist on any fragment in firstOutFragments or + * lastInFragments or false otherwise. + */ + private static boolean haveTransitions(SparseArray<Fragment> firstOutFragments, + SparseArray<Fragment> lastInFragments, boolean isBack) { + for (int i = firstOutFragments.size() - 1; i >= 0; i--) { + Fragment f = firstOutFragments.valueAt(i); + if (isBack) { + if (f.getReturnTransition() != null || + f.getSharedElementReturnTransition() != null) { + return true; + } + } else if (f.getExitTransition() != null) { + return true; + } + } + + for (int i = lastInFragments.size() - 1; i >= 0; i--) { + Fragment f = lastInFragments.valueAt(i); + if (isBack) { + if (f.getReenterTransition() != null) { + return true; + } + } else if (f.getEnterTransition() != null || + f.getSharedElementEnterTransition() != null) { + return true; + } } - View rootView = mManager.mContainer.findViewById(mSceneRoot); - if (!(rootView instanceof ViewGroup)) { - throw new IllegalArgumentException("SceneRoot is not a ViewGroup"); + return false; + } + + /** + * Finds the first removed fragment and last added fragments when popping the back stack. + * If none of the fragments have transitions, then both lists will be empty. + * + * @param firstOutFragments The list of first fragments to be removed, keyed on the + * container ID. This list will be modified by the method. + * @param lastInFragments The list of last fragments to be added, keyed on the + * container ID. This list will be modified by the method. + */ + public void calculateBackFragments(SparseArray<Fragment> firstOutFragments, + SparseArray<Fragment> lastInFragments) { + Op op = mHead; + while (op != null) { + switch (op.cmd) { + case OP_ADD: + setFirstOut(firstOutFragments, op.fragment); + break; + case OP_REPLACE: + if (op.removed != null) { + for (int i = op.removed.size() - 1; i >= 0; i--) { + setLastIn(lastInFragments, op.removed.get(i)); + } + } + setFirstOut(firstOutFragments, op.fragment); + break; + case OP_REMOVE: + setLastIn(lastInFragments, op.fragment); + break; + case OP_HIDE: + setLastIn(lastInFragments, op.fragment); + break; + case OP_SHOW: + setFirstOut(firstOutFragments, op.fragment); + break; + case OP_DETACH: + setLastIn(lastInFragments, op.fragment); + break; + case OP_ATTACH: + setFirstOut(firstOutFragments, op.fragment); + break; + } + + op = op.next; } + + if (!haveTransitions(firstOutFragments, lastInFragments, true)) { + firstOutFragments.clear(); + lastInFragments.clear(); + } + } + + /** + * When custom fragment transitions are used, this sets up the state for each transition + * and begins the transition. A different transition is started for each fragment container + * and consists of up to 3 different transitions: the exit transition, a shared element + * transition and an enter transition. + * + * <p>The exit transition operates against the leaf nodes of the first fragment + * with a view that was removed. If no such fragment was removed, then no exit + * transition is executed. The exit transition comes from the outgoing fragment.</p> + * + * <p>The enter transition operates against the last fragment that was added. If + * that fragment does not have a view or no fragment was added, then no enter + * transition is executed. The enter transition comes from the incoming fragment.</p> + * + * <p>The shared element transition operates against all views and comes either + * from the outgoing fragment or the incoming fragment, depending on whether this + * is going forward or popping the back stack. When going forward, the incoming + * fragment's enter shared element transition is used, but when going back, the + * outgoing fragment's return shared element transition is used. Shared element + * transitions only operate if there is both an incoming and outgoing fragment.</p> + * + * @param firstOutFragments The list of first fragments to be removed, keyed on the + * container ID. + * @param lastInFragments The list of last fragments to be added, keyed on the + * container ID. + * @param isBack true if this is popping the back stack or false if this is a + * forward operation. + * @return The TransitionState used to complete the operation of the transition + * in {@link #updateTransitionEndState(android.app.BackStackRecord.TransitionState, + * android.util.SparseArray, android.util.SparseArray, boolean)}. + */ + private TransitionState beginTransition(SparseArray<Fragment> firstOutFragments, + SparseArray<Fragment> lastInFragments, boolean isBack) { TransitionState state = new TransitionState(); - // get Transition scene root and create Transitions - state.sceneRoot = (ViewGroup) rootView; - state.sceneRoot.captureTransitioningViews(state.transitioningViews); - - state.exitTransition = TransitionInflater.from(mManager.mActivity) - .inflateTransition(mCustomTransition); - state.sharedElementTransition = TransitionInflater.from(mManager.mActivity) - .inflateTransition(mCustomTransition); - state.enterTransition = TransitionInflater.from(mManager.mActivity) - .inflateTransition(mCustomTransition); + // Adding a non-existent target view makes sure that the transitions don't target // any views by default. They'll only target the views we tell add. If we don't // add any, then no views will be targeted. - View nonExistentView = new View(mManager.mActivity); - state.enterTransition.addTarget(nonExistentView); - state.exitTransition.addTarget(nonExistentView); - state.sharedElementTransition.addTarget(nonExistentView); - - setSharedElementEpicenter(state.enterTransition, state); - - state.excludingTransition = new TransitionSet() - .addTransition(state.exitTransition) - .addTransition(state.enterTransition); + state.nonExistentView = new View(mManager.mActivity); + + ArrayMap<String, View> tempViews1 = new ArrayMap<String, View>(); + ArrayMap<String, View> tempViews2 = new ArrayMap<String, View>(); + ArrayList<String> tempNames = new ArrayList<String>(); + ArrayList<View> tempViewList = new ArrayList<View>(); + + // Go over all leaving fragments. + for (int i = 0; i < firstOutFragments.size(); i++) { + int containerId = firstOutFragments.keyAt(i); + configureTransitions(containerId, state, isBack, firstOutFragments, + lastInFragments, tempViews1, tempViews2, tempNames, tempViewList); + } - if (sourceNames != null) { - // Map shared elements. - state.sceneRoot.findNamedViews(state.namedViews); - state.namedViews.retainAll(sourceNames); - View epicenterView = state.namedViews.get(sourceNames.get(0)); - if (epicenterView != null) { - // The epicenter is only the first shared element. - setEpicenter(state.exitTransition, epicenterView); - setEpicenter(state.sharedElementTransition, epicenterView); + // Now go over all entering fragments that didn't have a leaving fragment. + for (int i = 0; i < lastInFragments.size(); i++) { + int containerId = lastInFragments.keyAt(i); + if (firstOutFragments.get(containerId) == null) { + configureTransitions(containerId, state, isBack, firstOutFragments, + lastInFragments, tempViews1, tempViews2, tempNames, tempViewList); } - state.transitioningViews.removeAll(state.namedViews.values()); - state.excludingTransition.addTransition(state.sharedElementTransition); - addTransitioningViews(state.sharedElementTransition, state.namedViews.values()); } - // Adds the (maybe) exiting views, not including the shared element. - // If some stay, that's ok. - addTransitioningViews(state.exitTransition, state.transitioningViews); + if (state.overallTransitions.size() == 0) { + state = null; + } + return state; + } - // Prepare for shared element name mapping. This could be chained in the case - // of popping several back stack states. - state.excludingTransition.setNameOverrides(new ArrayMap<String, String>()); - setNameOverrides(state, sourceNames, targetNames); + private static Transition getEnterTransition(Fragment inFragment, boolean isBack) { + if (inFragment == null) { + return null; + } + return isBack ? inFragment.getReenterTransition() : inFragment.getEnterTransition(); + } - // Don't include any subtree in the views that are hidden when capturing the - // view hierarchy transitions. They should be as if not there. - excludeHiddenFragments(state, true); + private static Transition getExitTransition(Fragment outFragment, boolean isBack) { + if (outFragment == null) { + return null; + } + return isBack ? outFragment.getReturnTransition() : outFragment.getExitTransition(); + } - TransitionManager.beginDelayedTransition(state.sceneRoot, state.excludingTransition); - return state; + private static Transition getSharedElementTransition(Fragment inFragment, Fragment outFragment, + boolean isBack) { + if (inFragment == null || outFragment == null) { + return null; + } + return isBack ? outFragment.getSharedElementReturnTransition() : + inFragment.getSharedElementEnterTransition(); } - private void updateTransitionEndState(TransitionState state, ArrayList<String> names) { - // Find all views that are entering. - ArrayList<View> enteringViews = new ArrayList<View>(); - state.sceneRoot.captureTransitioningViews(enteringViews); - enteringViews.removeAll(state.transitioningViews); - - if (names != null) { - // find all shared elements. - state.namedViews.clear(); - state.sceneRoot.findNamedViews(state.namedViews); - state.namedViews.retainAll(names); - if (!state.namedViews.isEmpty()) { - enteringViews.removeAll(state.namedViews.values()); - addTransitioningViews(state.sharedElementTransition, state.namedViews.values()); - // now we know the epicenter of the entering transition. - state.mEnteringEpicenterView = state.namedViews.get(names.get(0)); + private static Transition captureExitingViews(Transition exitTransition, Fragment outFragment, + ArrayList<View> viewList) { + if (exitTransition != null) { + View root = outFragment.getView(); + viewList.clear(); + root.captureTransitioningViews(viewList); + if (viewList.isEmpty()) { + exitTransition = null; + } else { + addTransitioningViews(exitTransition, viewList); + } + } + return exitTransition; + } + + private ArrayMap<String, View> remapSharedElements(TransitionState state, Fragment outFragment, + ArrayMap<String, View> namedViews, ArrayMap<String, View> tempViews2, boolean isBack) { + if (mSharedElementSourceNames != null) { + outFragment.getView().findNamedViews(namedViews); + if (isBack) { + namedViews.retainAll(mSharedElementTargetNames); + } else { + namedViews = remapNames(mSharedElementSourceNames, mSharedElementTargetNames, + namedViews, tempViews2); } } - // Add all entering views to the enter transition. - addTransitioningViews(state.enterTransition, enteringViews); + if (isBack) { + outFragment.mEnterTransitionListener.remapSharedElements( + mSharedElementTargetNames, namedViews); + setBackNameOverrides(state, namedViews, false); + } else { + outFragment.mExitTransitionListener.remapSharedElements( + mSharedElementTargetNames, namedViews); + setNameOverrides(state, namedViews, false); + } - // Don't allow capturing state for the newly-hidden fragments. - excludeHiddenFragments(state, false); + return namedViews; + } - // Allow capturing state for the newly-shown fragments - includeVisibleFragments(state.excludingTransition); + /** + * Prepares the enter transition by adding a non-existent view to the transition's target list + * and setting it epicenter callback. By adding a non-existent view to the target list, + * we can prevent any view from being targeted at the beginning of the transition. + * We will add to the views before the end state of the transition is captured so that the + * views will appear. At the start of the transition, we clear the list of targets so that + * we can restore the state of the transition and use it again. + */ + private void prepareEnterTransition(TransitionState state, final Transition enterTransition, + final View container, final Fragment inFragment) { + if (enterTransition != null) { + final ArrayList<View> enteringViews = new ArrayList<View>(); + final View nonExistentView = state.nonExistentView; + enterTransition.addTarget(state.nonExistentView); + enterTransition.addListener(new Transition.TransitionListenerAdapter() { + @Override + public void onTransitionStart(Transition transition) { + transition.removeListener(this); + transition.removeTarget(nonExistentView); + int numViews = enteringViews.size(); + for (int i = 0; i < numViews; i++) { + transition.removeTarget(enteringViews.get(i)); + } + } + }); + container.getViewTreeObserver().addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + container.getViewTreeObserver().removeOnPreDrawListener(this); + View view = inFragment.getView(); + if (view != null) { + view.captureTransitioningViews(enteringViews); + addTransitioningViews(enterTransition, enteringViews); + } + return true; + } + }); + setSharedElementEpicenter(enterTransition, state); + } } - private void addTransitioningViews(Transition transition, Collection<View> views) { - if (views.isEmpty()) { - // Add a view so that we can modify the valid views at the end of the - // fragment transaction. - transition.addTarget(new View(mManager.mActivity)); + private static Transition mergeTransitions(Transition enterTransition, + Transition exitTransition, Transition sharedElementTransition, Fragment inFragment, + boolean isBack) { + boolean overlap = true; + if (enterTransition != null && exitTransition != null) { + overlap = isBack ? inFragment.getAllowReturnTransitionOverlap() : + inFragment.getAllowEnterTransitionOverlap(); + } + + Transition transition; + if (overlap) { + transition = TransitionUtils.mergeTransitions(enterTransition, exitTransition, + sharedElementTransition); } else { - for (View view : views) { - transition.addTarget(view); + TransitionSet staggered = new TransitionSet() + .addTransition(exitTransition) + .addTransition(enterTransition) + .setOrdering(TransitionSet.ORDERING_SEQUENTIAL); + transition = TransitionUtils.mergeTransitions(staggered, sharedElementTransition); + } + return transition; + } + + /** + * Configures custom transitions for a specific fragment container. + * + * @param containerId The container ID of the fragments to configure the transition for. + * @param state The Transition State to be shared with {@link #updateTransitionEndState( + * android.app.BackStackRecord.TransitionState, android.util.SparseArray, + * android.util.SparseArray, boolean)} later. + * @param firstOutFragments The list of first fragments to be removed, keyed on the + * container ID. + * @param lastInFragments The list of last fragments to be added, keyed on the + * container ID. + * @param isBack true if this is popping the back stack or false if this is a + * forward operation. + * @param tempViews1 A temporary mapping of names to Views, used to avoid allocation + * inside a loop. + * @param tempViews2 A temporary mapping of names to Views, used to avoid allocation + * inside a loop. + * @param tempNames A temporary list of Strings, used to avoid allocation inside a loop. + * @param tempViewList A temporary list of Views, used to avoid allocation inside a loop. + */ + private void configureTransitions(int containerId, TransitionState state, boolean isBack, + SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments, + ArrayMap<String, View> tempViews1, ArrayMap<String, View> tempViews2, + ArrayList<String> tempNames, ArrayList<View> tempViewList) { + ViewGroup sceneRoot = (ViewGroup) mManager.mContainer.findViewById(containerId); + if (sceneRoot != null) { + Fragment inFragment = lastInFragments.get(containerId); + Fragment outFragment = firstOutFragments.get(containerId); + + Transition enterTransition = getEnterTransition(inFragment, isBack); + Transition sharedElementTransition = getSharedElementTransition(inFragment, outFragment, + isBack); + Transition exitTransition = getExitTransition(outFragment, isBack); + exitTransition = captureExitingViews(exitTransition, outFragment, tempViewList); + + ArrayMap<String, View> namedViews = tempViews1; + namedViews.clear(); + if (sharedElementTransition != null) { + namedViews = remapSharedElements(state, + outFragment, namedViews, tempViews2, isBack); + } + + // Notify the start of the transition. + SharedElementListener listener = isBack ? + outFragment.mEnterTransitionListener : + inFragment.mEnterTransitionListener; + tempNames.clear(); + tempNames.addAll(namedViews.keySet()); + tempViewList.clear(); + tempViewList.addAll(namedViews.values()); + listener.setSharedElementStart(tempNames, tempViewList, null); + + // Set the epicenter of the exit transition + if (mSharedElementTargetNames != null && exitTransition != null) { + View epicenterView = namedViews.get(mSharedElementTargetNames.get(0)); + if (epicenterView != null) { + setEpicenter(exitTransition, epicenterView); + } + } + + prepareEnterTransition(state, enterTransition, sceneRoot, inFragment); + + Transition transition = mergeTransitions(enterTransition, exitTransition, + sharedElementTransition, inFragment, isBack); + + if (transition != null) { + state.overallTransitions.put(containerId, transition); + transition.setNameOverrides(state.nameOverrides); + // We want to exclude hidden views later, so we need a non-null list in the + // transition now. + transition.excludeTarget(state.nonExistentView, true); + // Now exclude all currently hidden fragments. + excludeHiddenFragments(state, containerId, transition); + cleanupHiddenFragments(transition, state); + TransitionManager.beginDelayedTransition(sceneRoot, transition); } } } - private void excludeHiddenFragments(TransitionState state, boolean forceExclude) { - if (mManager.mAdded != null) { - for (int i = 0; i < mManager.mAdded.size(); i++) { - Fragment fragment = mManager.mAdded.get(i); - if (fragment.mView != null && fragment.mHidden - && (forceExclude || !state.hiddenViews.contains(fragment.mView))) { - state.excludingTransition.excludeTarget(fragment.mView, true); - state.hiddenViews.add(fragment.mView); + /** + * Remaps a name-to-View map, substituting different names for keys. + * + * @param inMap A list of keys found in the map, in the order in toGoInMap + * @param toGoInMap A list of keys to use for the new map, in the order of inMap + * @param namedViews The current mapping + * @param tempMap A temporary mapping that will be filled with the new values. + * @return tempMap after it has been mapped with the new names as keys. + */ + private static ArrayMap<String, View> remapNames(ArrayList<String> inMap, + ArrayList<String> toGoInMap, ArrayMap<String, View> namedViews, + ArrayMap<String, View> tempMap) { + tempMap.clear(); + if (!namedViews.isEmpty()) { + int numKeys = inMap.size(); + for (int i = 0; i < numKeys; i++) { + View view = namedViews.get(inMap.get(i)); + if (view != null) { + tempMap.put(toGoInMap.get(i), view); } } } - if (forceExclude && state.hiddenViews.isEmpty()) { - state.excludingTransition.excludeTarget(new View(mManager.mActivity), true); + return tempMap; + } + + /** + * After making all fragment changes, this updates the custom transitions to take into + * account the entering views and any remapping. + * + * @param state The transition State as returned from {@link #beginTransition( + * android.util.SparseArray, android.util.SparseArray, boolean)}. + * @param outFragments The list of first fragments to be removed, keyed on the + * container ID. + * @param inFragments The list of last fragments to be added, keyed on the + * container ID. + * @param isBack true if this is popping the back stack or false if this is a + * forward operation. + */ + private void updateTransitionEndState(TransitionState state, SparseArray<Fragment> outFragments, + SparseArray<Fragment> inFragments, boolean isBack) { + ArrayMap<String, View> tempViews1 = new ArrayMap<String, View>(); + ArrayMap<String, View> tempViews2 = new ArrayMap<String, View>(); + ArrayList<String> tempNames = new ArrayList<String>(); + ArrayList<View> tempViews = new ArrayList<View>(); + + int numInFragments = inFragments.size(); + for (int i = 0; i < numInFragments; i++) { + Fragment inFragment = inFragments.valueAt(i); + tempViews1.clear(); + ArrayMap<String, View> namedViews = mapEnteringSharedElements(inFragment, tempViews1, + tempViews2, isBack); + // remap shared elements and set the name mapping used in the shared element transition. + if (isBack) { + inFragment.mExitTransitionListener.remapSharedElements( + mSharedElementTargetNames, namedViews); + setBackNameOverrides(state, namedViews, true); + } else { + inFragment.mEnterTransitionListener.remapSharedElements( + mSharedElementTargetNames, namedViews); + setNameOverrides(state, namedViews, true); + } + + if (mSharedElementTargetNames != null && !namedViews.isEmpty()) { + // now we know the epicenter of the entering transition. + View epicenter = namedViews.get(mSharedElementTargetNames.get(0)); + if (epicenter != null) { + state.enteringEpicenterView = epicenter; + } + } + + int containerId = inFragments.keyAt(i); + SharedElementListener sharedElementListener = isBack ? + outFragments.get(containerId).mEnterTransitionListener : + inFragment.mEnterTransitionListener; + tempNames.clear(); + tempNames.addAll(namedViews.keySet()); + tempViews.clear(); + tempViews.addAll(namedViews.values()); + sharedElementListener.setSharedElementEnd(tempNames, tempViews, null); } + + // Don't include any newly-hidden fragments in the transition. + excludeHiddenFragments(state); } - private void includeVisibleFragments(Transition transition) { + private ArrayMap<String, View> mapEnteringSharedElements(Fragment inFragment, + ArrayMap<String, View> namedViews, ArrayMap<String, View> tempViews2, boolean isBack) { + View root = inFragment.getView(); + if (root != null) { + if (mSharedElementSourceNames != null) { + root.findNamedViews(namedViews); + if (isBack) { + namedViews = remapNames(mSharedElementSourceNames, + mSharedElementTargetNames, namedViews, tempViews2); + } else { + namedViews.retainAll(mSharedElementTargetNames); + } + } + } + return namedViews; + } + + private static void cleanupHiddenFragments(Transition transition, TransitionState state) { + final ArrayList<View> hiddenViews = state.hiddenFragmentViews; + transition.addListener(new Transition.TransitionListenerAdapter() { + @Override + public void onTransitionStart(Transition transition) { + transition.removeListener(this); + int numViews = hiddenViews.size(); + for (int i = 0; i < numViews; i++) { + transition.excludeTarget(hiddenViews.get(i), false); + } + } + }); + } + + private void excludeHiddenFragments(TransitionState state, int containerId, + Transition transition) { if (mManager.mAdded != null) { for (int i = 0; i < mManager.mAdded.size(); i++) { Fragment fragment = mManager.mAdded.get(i); - if (fragment.mView != null && !fragment.mHidden) { - transition.excludeTarget(fragment.mView, false); + if (fragment.mView != null && fragment.mContainer != null && + fragment.mContainerId == containerId) { + if (fragment.mHidden) { + if (!state.hiddenFragmentViews.contains(fragment.mView)) { + transition.excludeTarget(fragment.mView, true); + state.hiddenFragmentViews.add(fragment.mView); + } + } else { + transition.excludeTarget(fragment.mView, false); + state.hiddenFragmentViews.remove(fragment.mView); + } } } } } + private void excludeHiddenFragments(TransitionState state) { + int numTransitions = state.overallTransitions.size(); + for (int i = 0; i < numTransitions; i++) { + Transition transition = state.overallTransitions.valueAt(i); + int containerId = state.overallTransitions.keyAt(i); + excludeHiddenFragments(state, containerId, transition); + } + } + + private static void addTransitioningViews(Transition transition, final Collection<View> views) { + for (View view : views) { + transition.addTarget(view); + } + + transition.addListener(new Transition.TransitionListenerAdapter() { + @Override + public void onTransitionStart(Transition transition) { + transition.removeListener(this); + for (View view : views) { + transition.removeTarget(view); + } + } + }); + } + private static void setEpicenter(Transition transition, View view) { final Rect epicenter = new Rect(); view.getBoundsOnScreen(epicenter); @@ -1010,16 +1464,17 @@ final class BackStackRecord extends FragmentTransaction implements @Override public Rect onGetEpicenter(Transition transition) { - if (mEpicenter == null && state.mEnteringEpicenterView != null) { + if (mEpicenter == null && state.enteringEpicenterView != null) { mEpicenter = new Rect(); - state.mEnteringEpicenterView.getBoundsOnScreen(mEpicenter); + state.enteringEpicenterView.getBoundsOnScreen(mEpicenter); } return mEpicenter; } }); } - public TransitionState popFromBackStack(boolean doStateMove, TransitionState state) { + public TransitionState popFromBackStack(boolean doStateMove, TransitionState state, + SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) { if (FragmentManagerImpl.DEBUG) { Log.v(TAG, "popFromBackStack: " + this); LogWriter logw = new LogWriter(Log.VERBOSE, TAG); @@ -1029,8 +1484,10 @@ final class BackStackRecord extends FragmentTransaction implements } if (state == null) { - state = beginTransition(mSharedElementTargetNames, mSharedElementSourceNames); - } else { + if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) { + state = beginTransition(firstOutFragments, lastInFragments, true); + } + } else if (!doStateMove) { setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames); } @@ -1110,7 +1567,7 @@ final class BackStackRecord extends FragmentTransaction implements mManager.moveToState(mManager.mCurState, FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true); if (state != null) { - updateTransitionEndState(state, mSharedElementSourceNames); + updateTransitionEndState(state, firstOutFragments, lastInFragments, true); state = null; } } @@ -1122,15 +1579,17 @@ final class BackStackRecord extends FragmentTransaction implements return state; } - private static void setNameOverride(Transition transition, String source, String target) { - ArrayMap<String, String> overrides = transition.getNameOverrides(); - for (int index = 0; index < overrides.size(); index++) { - if (source.equals(overrides.valueAt(index))) { - overrides.setValueAt(index, target); - return; + private static void setNameOverride(ArrayMap<String, String> overrides, + String source, String target) { + if (source != null && target != null && !source.equals(target)) { + for (int index = 0; index < overrides.size(); index++) { + if (source.equals(overrides.valueAt(index))) { + overrides.setValueAt(index, target); + return; + } } + overrides.put(source, target); } - overrides.put(source, target); } private static void setNameOverrides(TransitionState state, ArrayList<String> sourceNames, @@ -1139,7 +1598,36 @@ final class BackStackRecord extends FragmentTransaction implements for (int i = 0; i < sourceNames.size(); i++) { String source = sourceNames.get(i); String target = targetNames.get(i); - setNameOverride(state.excludingTransition, source, target); + setNameOverride(state.nameOverrides, source, target); + } + } + } + + private void setBackNameOverrides(TransitionState state, ArrayMap<String, View> namedViews, + boolean isEnd) { + int count = mSharedElementTargetNames.size(); + for (int i = 0; i < count; i++) { + String source = mSharedElementSourceNames.get(i); + String originalTarget = mSharedElementTargetNames.get(i); + String target = namedViews.get(originalTarget).getTransitionName(); + if (isEnd) { + setNameOverride(state.nameOverrides, source, target); + } else { + setNameOverride(state.nameOverrides, target, source); + } + } + } + + private void setNameOverrides(TransitionState state, ArrayMap<String, View> namedViews, + boolean isEnd) { + int count = namedViews.size(); + for (int i = 0; i < count; i++) { + String source = namedViews.keyAt(i); + String target = namedViews.valueAt(i).getTransitionName(); + if (isEnd) { + setNameOverride(state.nameOverrides, source, target); + } else { + setNameOverride(state.nameOverrides, target, source); } } } @@ -1161,14 +1649,11 @@ final class BackStackRecord extends FragmentTransaction implements } public class TransitionState { - public ArrayList<View> hiddenViews = new ArrayList<View>(); - public ArrayList<View> transitioningViews = new ArrayList<View>(); - public ArrayMap<String, View> namedViews = new ArrayMap<String, View>(); - public Transition exitTransition; - public Transition sharedElementTransition; - public Transition enterTransition; - public TransitionSet excludingTransition; - public ViewGroup sceneRoot; - public View mEnteringEpicenterView; + public SparseArray<Transition> overallTransitions = new SparseArray<Transition>(); + public ArrayMap<String, String> nameOverrides = new ArrayMap<String, String>(); + public ArrayList<View> hiddenFragmentViews = new ArrayList<View>(); + + public View enteringEpicenterView; + public View nonExistentView; } } diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index 5a6898d..47d3fd6 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -194,10 +194,12 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { @Override public boolean onPreDraw() { getDecor().getViewTreeObserver().removeOnPreDrawListener(this); - Bundle state = captureSharedElementState(); - setSharedElementMatrices(); - moveSharedElementsToOverlay(); - mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state); + if (mResultReceiver != null) { + Bundle state = captureSharedElementState(); + setSharedElementMatrices(); + moveSharedElementsToOverlay(); + mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state); + } return true; } }); diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java index f31800d..3760b96 100644 --- a/core/java/android/app/ExitTransitionCoordinator.java +++ b/core/java/android/app/ExitTransitionCoordinator.java @@ -19,6 +19,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Intent; +import android.graphics.Color; import android.graphics.Matrix; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; @@ -222,10 +223,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { delayCancel(); moveSharedElementsToOverlay(); if (getDecor().getBackground() == null) { - ColorDrawable black = new ColorDrawable(0xFF000000); - black.setAlpha(0); - getWindow().setBackgroundDrawable(black); - black.setAlpha(255); + getWindow().setBackgroundDrawable(new ColorDrawable(Color.BLACK)); } ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(mActivity, this, mAllSharedElementNames, resultCode, data); diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index 2ff3d57..dbee81e 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -23,10 +23,14 @@ import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; +import android.content.res.TypedArray; import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.transition.Transition; +import android.transition.TransitionInflater; +import android.transition.TransitionSet; import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.AttributeSet; @@ -58,11 +62,11 @@ final class FragmentState implements Parcelable { final boolean mRetainInstance; final boolean mDetached; final Bundle mArguments; - + Bundle mSavedFragmentState; - + Fragment mInstance; - + public FragmentState(Fragment frag) { mClassName = frag.getClass().getName(); mIndex = frag.mIndex; @@ -74,7 +78,7 @@ final class FragmentState implements Parcelable { mDetached = frag.mDetached; mArguments = frag.mArguments; } - + public FragmentState(Parcel in) { mClassName = in.readString(); mIndex = in.readInt(); @@ -87,18 +91,18 @@ final class FragmentState implements Parcelable { mArguments = in.readBundle(); mSavedFragmentState = in.readBundle(); } - + public Fragment instantiate(Activity activity, Fragment parent) { if (mInstance != null) { return mInstance; } - + if (mArguments != null) { mArguments.setClassLoader(activity.getClassLoader()); } - + mInstance = Fragment.instantiate(activity, mClassName, mArguments); - + if (mSavedFragmentState != null) { mSavedFragmentState.setClassLoader(activity.getClassLoader()); mInstance.mSavedFragmentState = mSavedFragmentState; @@ -117,7 +121,7 @@ final class FragmentState implements Parcelable { return mInstance; } - + public int describeContents() { return 0; } @@ -134,13 +138,13 @@ final class FragmentState implements Parcelable { dest.writeBundle(mArguments); dest.writeBundle(mSavedFragmentState); } - + public static final Parcelable.Creator<FragmentState> CREATOR = new Parcelable.Creator<FragmentState>() { public FragmentState createFromParcel(Parcel in) { return new FragmentState(in); } - + public FragmentState[] newArray(int size) { return new FragmentState[size]; } @@ -299,17 +303,17 @@ final class FragmentState implements Parcelable { * how you can determine if a fragment placed in a container is no longer * running in a layout with that container and avoid creating its view hierarchy * in that case.) - * + * * <p>The attributes of the <fragment> tag are used to control the * LayoutParams provided when attaching the fragment's view to the parent * container. They can also be parsed by the fragment in {@link #onInflate} * as parameters. - * + * * <p>The fragment being instantiated must have some kind of unique identifier * so that it can be re-associated with a previous instance if the parent * activity needs to be destroyed and recreated. This can be provided these * ways: - * + * * <ul> * <li>If nothing is explicitly supplied, the view ID of the container will * be used. @@ -318,7 +322,7 @@ final class FragmentState implements Parcelable { * <li><code>android:id</code> can be used in <fragment> to provide * a specific identifier for the fragment. * </ul> - * + * * <a name="BackStack"></a> * <h3>Back Stack</h3> * @@ -347,7 +351,7 @@ final class FragmentState implements Parcelable { public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListener { private static final ArrayMap<String, Class<?>> sClassMap = new ArrayMap<String, Class<?>>(); - + static final int INVALID_STATE = -1; // Invalid state used as a null value. static final int INITIALIZING = 0; // Not yet created. static final int CREATED = 1; // Created. @@ -355,9 +359,11 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene static final int STOPPED = 3; // Fully created, not started. static final int STARTED = 4; // Created and started, not resumed. static final int RESUMED = 5; // Created started and resumed. - + + private static final Transition USE_DEFAULT_TRANSITION = new TransitionSet(); + int mState = INITIALIZING; - + // Non-null if the fragment's view hierarchy is currently animating away, // meaning we need to wait a bit on completely destroying it. This is the // animation that is running. @@ -370,13 +376,13 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene // When instantiated from saved state, this is the saved state. Bundle mSavedFragmentState; SparseArray<Parcelable> mSavedViewState; - + // Index into active fragment array. int mIndex = -1; - + // Internal unique name for this fragment; String mWho; - + // Construction arguments; Bundle mArguments; @@ -391,25 +397,25 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene // True if the fragment is in the list of added fragments. boolean mAdded; - + // If set this fragment is being removed from its activity. boolean mRemoving; // True if the fragment is in the resumed state. boolean mResumed; - + // Set to true if this fragment was instantiated from a layout file. boolean mFromLayout; - + // Set to true when the view has actually been inflated in its layout. boolean mInLayout; // True if this fragment has been restored from previously saved state. boolean mRestored; - + // Number of active back stack entries this fragment is in. int mBackStackNesting; - + // The fragment manager we are associated with. Set as soon as the // fragment is used in a transaction; cleared after it has been removed // from all transactions. @@ -428,29 +434,29 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene // was dynamically added to the view hierarchy, or the ID supplied in // layout. int mFragmentId; - + // When a fragment is being dynamically added to the view hierarchy, this // is the identifier of the parent container it is being added to. int mContainerId; - + // The optional named tag for this fragment -- usually used to find // fragments that are not part of the layout. String mTag; - + // Set to true when the app has requested that this fragment be hidden // from the user. boolean mHidden; - + // Set to true when the app has requested that this fragment be detached. boolean mDetached; // If set this fragment would like its instance retained across // configuration changes. boolean mRetainInstance; - + // If set this fragment is being retained across the current config change. boolean mRetaining; - + // If set this fragment has menu items to contribute. boolean mHasMenu; @@ -459,16 +465,16 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene // Used to verify that subclasses call through to super class. boolean mCalled; - + // If app has requested a specific animation, this is the one to use. int mNextAnim; - + // The parent container of the fragment after dynamically added to UI. ViewGroup mContainer; - + // The View generated for this fragment. View mView; - + // Whether this fragment should defer starting until after other fragments // have been started and their loaders are finished. boolean mDeferStart; @@ -479,7 +485,19 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene LoaderManagerImpl mLoaderManager; boolean mLoadersStarted; boolean mCheckedForLoaderManager; - + + private Transition mEnterTransition = null; + private Transition mReturnTransition = USE_DEFAULT_TRANSITION; + private Transition mExitTransition = null; + private Transition mReenterTransition = USE_DEFAULT_TRANSITION; + private Transition mSharedElementEnterTransition = null; + private Transition mSharedElementReturnTransition = USE_DEFAULT_TRANSITION; + private Boolean mAllowReturnTransitionOverlap; + private Boolean mAllowEnterTransitionOverlap; + + SharedElementListener mEnterTransitionListener = SharedElementListener.NULL_LISTENER; + SharedElementListener mExitTransitionListener = SharedElementListener.NULL_LISTENER; + /** * State information that has been retrieved from a fragment instance * through {@link FragmentManager#saveFragmentInstanceState(Fragment) @@ -543,7 +561,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * will not be called when the fragment is re-instantiated; instead, * arguments can be supplied by the caller with {@link #setArguments} * and later retrieved by the Fragment with {@link #getArguments}. - * + * * <p>Applications should generally not implement a constructor. The * first place application code an run where the fragment is ready to * be used is in {@link #onAttach(Activity)}, the point where the fragment @@ -609,7 +627,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene + " empty constructor that is public", e); } } - + final void restoreViewState(Bundle savedInstanceState) { if (mSavedViewState != null) { mView.restoreHierarchyState(mSavedViewState); @@ -649,7 +667,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene @Override final public int hashCode() { return super.hashCode(); } - + @Override public String toString() { StringBuilder sb = new StringBuilder(128); @@ -669,7 +687,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene sb.append('}'); return sb.toString(); } - + /** * Return the identifier this fragment is known by. This is either * the android:id value supplied in a layout or the container view ID @@ -678,14 +696,14 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene final public int getId() { return mFragmentId; } - + /** * Get the tag name of the fragment, if specified. */ final public String getTag() { return mTag; } - + /** * Supply the construction arguments for this fragment. This can only * be called before the fragment has been attached to its activity; that @@ -760,7 +778,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene final public Activity getActivity() { return mActivity; } - + /** * Return <code>getActivity().getResources()</code>. */ @@ -770,7 +788,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene } return mActivity.getResources(); } - + /** * Return a localized, styled CharSequence from the application's package's * default string table. @@ -870,7 +888,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene final public boolean isRemoving() { return mRemoving; } - + /** * Return true if the layout is included as part of an activity view * hierarchy via the <fragment> tag. This will always be true when @@ -889,7 +907,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene final public boolean isResumed() { return mResumed; } - + /** * Return true if the fragment is currently visible to the user. This means * it: (1) has been added, (2) has its view attached to the window, and @@ -899,7 +917,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene return isAdded() && !isHidden() && mView != null && mView.getWindowToken() != null && mView.getVisibility() == View.VISIBLE; } - + /** * Return true if the fragment has been hidden. By default fragments * are shown. You can find out about changes to this state with @@ -910,7 +928,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene final public boolean isHidden() { return mHidden; } - + /** * Called when the hidden state (as returned by {@link #isHidden()} of * the fragment has changed. Fragments start out not hidden; this will @@ -920,7 +938,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene */ public void onHiddenChanged(boolean hidden) { } - + /** * Control whether a fragment instance is retained across Activity * re-creation (such as from a configuration change). This can only @@ -942,16 +960,16 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene } mRetainInstance = retain; } - + final public boolean getRetainInstance() { return mRetainInstance; } - + /** * Report that this fragment would like to participate in populating * the options menu by receiving a call to {@link #onCreateOptionsMenu} * and related methods. - * + * * @param hasMenu If true, the fragment has menu items to contribute. */ public void setHasOptionsMenu(boolean hasMenu) { @@ -1034,7 +1052,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene public void startActivity(Intent intent) { startActivity(intent, null); } - + /** * Call {@link Activity#startActivity(Intent, Bundle)} from the fragment's * containing Activity. @@ -1081,13 +1099,13 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene mActivity.startActivityFromFragment(this, intent, requestCode, options); } } - + /** * Receive the result from a previous call to * {@link #startActivityForResult(Intent, int)}. This follows the * related Activity API as described there in * {@link Activity#onActivityResult(int, int, Intent)}. - * + * * @param requestCode The integer request code originally supplied to * startActivityForResult(), allowing you to identify who this * result came from. @@ -1098,7 +1116,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene */ public void onActivityResult(int requestCode, int resultCode, Intent data) { } - + /** * @hide Hack so that DialogFragment can make its Dialog before creating * its views, and the view construction can use the dialog's context for @@ -1115,7 +1133,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene return mActivity.getLayoutInflater(); } } - + /** * @deprecated Use {@link #onInflate(Activity, AttributeSet, Bundle)} instead. */ @@ -1131,7 +1149,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * tag in a layout file. Note this is <em>before</em> the fragment's * {@link #onAttach(Activity)} has been called; all you should do here is * parse the attributes and save them away. - * + * * <p>This is called every time the fragment is inflated, even if it is * being inflated into a new instance with saved state. It typically makes * sense to re-parse the parameters each time, to allow them to change with @@ -1169,8 +1187,33 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene public void onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState) { onInflate(attrs, savedInstanceState); mCalled = true; + + TypedArray a = activity.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.Fragment); + mEnterTransition = loadTransition(activity, a, mEnterTransition, null, + com.android.internal.R.styleable.Fragment_fragmentEnterTransition); + mReturnTransition = loadTransition(activity, a, mReturnTransition, USE_DEFAULT_TRANSITION, + com.android.internal.R.styleable.Fragment_fragmentReturnTransition); + mExitTransition = loadTransition(activity, a, mExitTransition, null, + com.android.internal.R.styleable.Fragment_fragmentExitTransition); + mReenterTransition = loadTransition(activity, a, mReenterTransition, USE_DEFAULT_TRANSITION, + com.android.internal.R.styleable.Fragment_fragmentReenterTransition); + mSharedElementEnterTransition = loadTransition(activity, a, mSharedElementEnterTransition, + null, com.android.internal.R.styleable.Fragment_fragmentSharedElementEnterTransition); + mSharedElementReturnTransition = loadTransition(activity, a, mSharedElementReturnTransition, + USE_DEFAULT_TRANSITION, + com.android.internal.R.styleable.Fragment_fragmentSharedElementReturnTransition); + if (mAllowEnterTransitionOverlap == null) { + mAllowEnterTransitionOverlap = a.getBoolean( + com.android.internal.R.styleable.Fragment_fragmentAllowEnterTransitionOverlap, true); + } + if (mAllowReturnTransitionOverlap == null) { + mAllowReturnTransitionOverlap = a.getBoolean( + com.android.internal.R.styleable.Fragment_fragmentAllowReturnTransitionOverlap, true); + } + a.recycle(); } - + /** * Called when a fragment is first attached to its activity. * {@link #onCreate(Bundle)} will be called after this. @@ -1178,25 +1221,25 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene public void onAttach(Activity activity) { mCalled = true; } - + /** * Called when a fragment loads an animation. */ public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) { return null; } - + /** * Called to do initial creation of a fragment. This is called after * {@link #onAttach(Activity)} and before * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}. - * + * * <p>Note that this can be called while the fragment's activity is * still in the process of being created. As such, you can not rely * on things like the activity's content view hierarchy being initialized * at this point. If you want to do work once the activity itself is * created, see {@link #onActivityCreated(Bundle)}. - * + * * @param savedInstanceState If the fragment is being re-created from * a previous saved state, this is the state. */ @@ -1209,10 +1252,10 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * This is optional, and non-graphical fragments can return null (which * is the default implementation). This will be called between * {@link #onCreate(Bundle)} and {@link #onActivityCreated(Bundle)}. - * + * * <p>If you return a View from here, you will later be called in * {@link #onDestroyView} when the view is being released. - * + * * @param inflater The LayoutInflater object that can be used to inflate * any views in the fragment, * @param container If non-null, this is the parent view that the fragment's @@ -1220,7 +1263,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * but this can be used to generate the LayoutParams of the view. * @param savedInstanceState If non-null, this fragment is being re-constructed * from a previous saved state as given here. - * + * * @return Return the View for the fragment's UI, or null. */ @Nullable @@ -1241,18 +1284,18 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene */ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { } - + /** * Get the root view for the fragment's layout (the one returned by {@link #onCreateView}), * if provided. - * + * * @return The fragment's root view, or null if it has no layout. */ @Nullable public View getView() { return mView; } - + /** * Called when the fragment's activity has been created and this * fragment's view hierarchy instantiated. It can be used to do final @@ -1292,7 +1335,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene */ public void onStart() { mCalled = true; - + if (!mLoadersStarted) { mLoadersStarted = true; if (!mCheckedForLoaderManager) { @@ -1304,7 +1347,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene } } } - + /** * Called when the fragment is visible to the user and actively running. * This is generally @@ -1314,7 +1357,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene public void onResume() { mCalled = true; } - + /** * Called to ask the fragment to save its current dynamic state, so it * can later be reconstructed in a new instance of its process is @@ -1336,11 +1379,11 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene */ public void onSaveInstanceState(Bundle outState) { } - + public void onConfigurationChanged(Configuration newConfig) { mCalled = true; } - + /** * Called when the Fragment is no longer resumed. This is generally * tied to {@link Activity#onPause() Activity.onPause} of the containing @@ -1349,7 +1392,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene public void onPause() { mCalled = true; } - + /** * Called when the Fragment is no longer started. This is generally * tied to {@link Activity#onStop() Activity.onStop} of the containing @@ -1358,11 +1401,11 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene public void onStop() { mCalled = true; } - + public void onLowMemory() { mCalled = true; } - + public void onTrimMemory(int level) { mCalled = true; } @@ -1379,7 +1422,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene public void onDestroyView() { mCalled = true; } - + /** * Called when the fragment is no longer in use. This is called * after {@link #onStop()} and before {@link #onDetach()}. @@ -1434,16 +1477,16 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene public void onDetach() { mCalled = true; } - + /** * Initialize the contents of the Activity's standard options menu. You * should place your menu items in to <var>menu</var>. For this method * to be called, you must have first called {@link #setHasOptionsMenu}. See * {@link Activity#onCreateOptionsMenu(Menu) Activity.onCreateOptionsMenu} * for more information. - * + * * @param menu The options menu in which you place your items. - * + * * @see #setHasOptionsMenu * @see #onPrepareOptionsMenu * @see #onOptionsItemSelected @@ -1458,10 +1501,10 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * dynamically modify the contents. See * {@link Activity#onPrepareOptionsMenu(Menu) Activity.onPrepareOptionsMenu} * for more information. - * + * * @param menu The options menu as last shown or first initialized by * onCreateOptionsMenu(). - * + * * @see #setHasOptionsMenu * @see #onCreateOptionsMenu */ @@ -1477,7 +1520,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene */ public void onDestroyOptionsMenu() { } - + /** * This hook is called whenever an item in your options menu is selected. * The default implementation simply returns false to have the normal @@ -1485,15 +1528,15 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * its Handler as appropriate). You can use this method for any items * for which you would like to do processing without those other * facilities. - * + * * <p>Derived classes should call through to the base class for it to * perform the default menu handling. - * + * * @param item The menu item that was selected. - * + * * @return boolean Return false to allow normal menu processing to * proceed, true to consume it here. - * + * * @see #onCreateOptionsMenu */ public boolean onOptionsItemSelected(MenuItem item) { @@ -1503,13 +1546,13 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene /** * This hook is called whenever the options menu is being closed (either by the user canceling * the menu with the back/menu button, or when an item is selected). - * + * * @param menu The options menu as last shown or first initialized by * onCreateOptionsMenu(). */ public void onOptionsMenuClosed(Menu menu) { } - + /** * Called when a context menu for the {@code view} is about to be shown. * Unlike {@link #onCreateOptionsMenu}, this will be called every @@ -1537,25 +1580,25 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * {@link OnCreateContextMenuListener} on the view to this fragment, so * {@link #onCreateContextMenu(ContextMenu, View, ContextMenuInfo)} will be * called when it is time to show the context menu. - * + * * @see #unregisterForContextMenu(View) * @param view The view that should show a context menu. */ public void registerForContextMenu(View view) { view.setOnCreateContextMenuListener(this); } - + /** * Prevents a context menu to be shown for the given view. This method will * remove the {@link OnCreateContextMenuListener} on the view. - * + * * @see #registerForContextMenu(View) * @param view The view that should stop showing a context menu. */ public void unregisterForContextMenu(View view) { view.setOnCreateContextMenuListener(null); } - + /** * This hook is called whenever an item in a context menu is selected. The * default implementation simply returns false to have the normal processing @@ -1568,7 +1611,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * <p> * Derived classes should call through to the base class for it to perform * the default menu handling. - * + * * @param item The context menu item that was selected. * @return boolean Return false to allow normal context menu processing to * proceed, true to consume it here. @@ -1576,7 +1619,284 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene public boolean onContextItemSelected(MenuItem item) { return false; } - + + /** + * When custom transitions are used with Fragments, the enter transition listener + * is called when this Fragment is attached or detached when not popping the back stack. + * + * @param listener Used to manipulate the shared element transitions on this Fragment + * when added not as a pop from the back stack. + */ + public void setEnterSharedElementTransitionListener(SharedElementListener listener) { + if (listener == null) { + listener = SharedElementListener.NULL_LISTENER; + } + mEnterTransitionListener = listener; + } + + /** + * When custom transitions are used with Fragments, the exit transition listener + * is called when this Fragment is attached or detached when popping the back stack. + * + * @param listener Used to manipulate the shared element transitions on this Fragment + * when added as a pop from the back stack. + */ + public void setExitSharedElementTransitionListener(SharedElementListener listener) { + if (listener == null) { + listener = SharedElementListener.NULL_LISTENER; + } + mExitTransitionListener = listener; + } + + /** + * Sets the Transition that will be used to move Views into the initial scene. The entering + * Views will be those that are regular Views or ViewGroups that have + * {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend + * {@link android.transition.Visibility} as entering is governed by changing visibility from + * {@link View#INVISIBLE} to {@link View#VISIBLE}. If <code>transition</code> is null, + * entering Views will remain unaffected. + * + * @param transition The Transition to use to move Views into the initial Scene. + * @attr ref android.R.styleable#Fragment_fragmentEnterTransition + */ + public void setEnterTransition(Transition transition) { + mEnterTransition = transition; + } + + /** + * Returns the Transition that will be used to move Views into the initial scene. The entering + * Views will be those that are regular Views or ViewGroups that have + * {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend + * {@link android.transition.Visibility} as entering is governed by changing visibility from + * {@link View#INVISIBLE} to {@link View#VISIBLE}. + * + * @return the Transition to use to move Views into the initial Scene. + * @attr ref android.R.styleable#Fragment_fragmentEnterTransition + */ + public Transition getEnterTransition() { + return mEnterTransition; + } + + /** + * Sets the Transition that will be used to move Views out of the scene when the Fragment is + * preparing to be removed, hidden, or detached because of popping the back stack. The exiting + * Views will be those that are regular Views or ViewGroups that have + * {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend + * {@link android.transition.Visibility} as entering is governed by changing visibility from + * {@link View#VISIBLE} to {@link View#INVISIBLE}. If <code>transition</code> is null, + * entering Views will remain unaffected. If nothing is set, the default will be to + * use the same value as set in {@link #setEnterTransition(android.transition.Transition)}. + * + * @param transition The Transition to use to move Views out of the Scene when the Fragment + * is preparing to close. + * @attr ref android.R.styleable#Fragment_fragmentExitTransition + */ + public void setReturnTransition(Transition transition) { + mReturnTransition = transition; + } + + /** + * Returns the Transition that will be used to move Views out of the scene when the Fragment is + * preparing to be removed, hidden, or detached because of popping the back stack. The exiting + * Views will be those that are regular Views or ViewGroups that have + * {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend + * {@link android.transition.Visibility} as entering is governed by changing visibility from + * {@link View#VISIBLE} to {@link View#INVISIBLE}. If <code>transition</code> is null, + * entering Views will remain unaffected. + * + * @return the Transition to use to move Views out of the Scene when the Fragment + * is preparing to close. + * @attr ref android.R.styleable#Fragment_fragmentExitTransition + */ + public Transition getReturnTransition() { + return mReturnTransition == USE_DEFAULT_TRANSITION ? getEnterTransition() + : mReturnTransition; + } + + /** + * Sets the Transition that will be used to move Views out of the scene when the + * fragment is removed, hidden, or detached when not popping the back stack. + * The exiting Views will be those that are regular Views or ViewGroups that + * have {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend + * {@link android.transition.Visibility} as exiting is governed by changing visibility + * from {@link View#VISIBLE} to {@link View#INVISIBLE}. If transition is null, the views will + * remain unaffected. + * + * @param transition The Transition to use to move Views out of the Scene when the Fragment + * is being closed not due to popping the back stack. + * @attr ref android.R.styleable#Fragment_fragmentExitTransition + */ + public void setExitTransition(Transition transition) { + mExitTransition = transition; + } + + /** + * Returns the Transition that will be used to move Views out of the scene when the + * fragment is removed, hidden, or detached when not popping the back stack. + * The exiting Views will be those that are regular Views or ViewGroups that + * have {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend + * {@link android.transition.Visibility} as exiting is governed by changing visibility + * from {@link View#VISIBLE} to {@link View#INVISIBLE}. If transition is null, the views will + * remain unaffected. + * + * @return the Transition to use to move Views out of the Scene when the Fragment + * is being closed not due to popping the back stack. + * @attr ref android.R.styleable#Fragment_fragmentExitTransition + */ + public Transition getExitTransition() { + return mExitTransition; + } + + /** + * Sets the Transition that will be used to move Views in to the scene when returning due + * to popping a back stack. The entering Views will be those that are regular Views + * or ViewGroups that have {@link ViewGroup#isTransitionGroup} return true. Typical Transitions + * will extend {@link android.transition.Visibility} as exiting is governed by changing + * visibility from {@link View#VISIBLE} to {@link View#INVISIBLE}. If transition is null, + * the views will remain unaffected. If nothing is set, the default will be to use the same + * transition as {@link #setExitTransition(android.transition.Transition)}. + * + * @param transition The Transition to use to move Views into the scene when reentering from a + * previously-started Activity. + * @attr ref android.R.styleable#Fragment_fragmentReenterTransition + */ + public void setReenterTransition(Transition transition) { + mReenterTransition = transition; + } + + /** + * Returns the Transition that will be used to move Views in to the scene when returning due + * to popping a back stack. The entering Views will be those that are regular Views + * or ViewGroups that have {@link ViewGroup#isTransitionGroup} return true. Typical Transitions + * will extend {@link android.transition.Visibility} as exiting is governed by changing + * visibility from {@link View#VISIBLE} to {@link View#INVISIBLE}. If transition is null, + * the views will remain unaffected. If nothing is set, the default will be to use the same + * transition as {@link #setExitTransition(android.transition.Transition)}. + * + * @return the Transition to use to move Views into the scene when reentering from a + * previously-started Activity. + * @attr ref android.R.styleable#Fragment_fragmentReenterTransition + */ + public Transition getReenterTransition() { + return mReenterTransition == USE_DEFAULT_TRANSITION ? getExitTransition() + : mReenterTransition; + } + + /** + * Sets the Transition that will be used for shared elements transferred into the content + * Scene. Typical Transitions will affect size and location, such as + * {@link android.transition.ChangeBounds}. A null + * value will cause transferred shared elements to blink to the final position. + * + * @param transition The Transition to use for shared elements transferred into the content + * Scene. + * @attr ref android.R.styleable#Fragment_fragmentSharedElementEnterTransition + */ + public void setSharedElementEnterTransition(Transition transition) { + mSharedElementEnterTransition = transition; + } + + /** + * Returns the Transition that will be used for shared elements transferred into the content + * Scene. Typical Transitions will affect size and location, such as + * {@link android.transition.ChangeBounds}. A null + * value will cause transferred shared elements to blink to the final position. + * + * @return The Transition to use for shared elements transferred into the content + * Scene. + * @attr ref android.R.styleable#Fragment_fragmentSharedElementEnterTransition + */ + public Transition getSharedElementEnterTransition() { + return mSharedElementEnterTransition; + } + + /** + * Sets the Transition that will be used for shared elements transferred back during a + * pop of the back stack. This Transition acts in the leaving Fragment. + * Typical Transitions will affect size and location, such as + * {@link android.transition.ChangeBounds}. A null + * value will cause transferred shared elements to blink to the final position. + * If no value is set, the default will be to use the same value as + * {@link #setSharedElementEnterTransition(android.transition.Transition)}. + * + * @param transition The Transition to use for shared elements transferred out of the content + * Scene. + * @attr ref android.R.styleable#Fragment_fragmentSharedElementReturnTransition + */ + public void setSharedElementReturnTransition(Transition transition) { + mSharedElementReturnTransition = transition; + } + + /** + * Return the Transition that will be used for shared elements transferred back during a + * pop of the back stack. This Transition acts in the leaving Fragment. + * Typical Transitions will affect size and location, such as + * {@link android.transition.ChangeBounds}. A null + * value will cause transferred shared elements to blink to the final position. + * If no value is set, the default will be to use the same value as + * {@link #setSharedElementEnterTransition(android.transition.Transition)}. + * + * @return The Transition to use for shared elements transferred out of the content + * Scene. + * @attr ref android.R.styleable#Fragment_fragmentSharedElementReturnTransition + */ + public Transition getSharedElementReturnTransition() { + return mSharedElementReturnTransition == USE_DEFAULT_TRANSITION ? + getSharedElementEnterTransition() : mSharedElementReturnTransition; + } + + /** + * Sets whether the the exit transition and enter transition overlap or not. + * When true, the enter transition will start as soon as possible. When false, the + * enter transition will wait until the exit transition completes before starting. + * + * @param allow true to start the enter transition when possible or false to + * wait until the exiting transition completes. + * @attr ref android.R.styleable#Fragment_fragmentAllowEnterTransitionOverlap + */ + public void setAllowEnterTransitionOverlap(boolean allow) { + mAllowEnterTransitionOverlap = allow; + } + + /** + * Returns whether the the exit transition and enter transition overlap or not. + * When true, the enter transition will start as soon as possible. When false, the + * enter transition will wait until the exit transition completes before starting. + * + * @return true when the enter transition should start as soon as possible or false to + * when it should wait until the exiting transition completes. + * @attr ref android.R.styleable#Fragment_fragmentAllowEnterTransitionOverlap + */ + public boolean getAllowEnterTransitionOverlap() { + return (mAllowEnterTransitionOverlap == null) ? true : mAllowEnterTransitionOverlap; + } + + /** + * Sets whether the the return transition and reenter transition overlap or not. + * When true, the reenter transition will start as soon as possible. When false, the + * reenter transition will wait until the return transition completes before starting. + * + * @param allow true to start the reenter transition when possible or false to wait until the + * return transition completes. + * @attr ref android.R.styleable#Fragment_fragmentAllowReturnTransitionOverlap + */ + public void setAllowReturnTransitionOverlap(boolean allow) { + mAllowReturnTransitionOverlap = allow; + } + + /** + * Returns whether the the return transition and reenter transition overlap or not. + * When true, the reenter transition will start as soon as possible. When false, the + * reenter transition will wait until the return transition completes before starting. + * + * @return true to start the reenter transition when possible or false to wait until the + * return transition completes. + * @attr ref android.R.styleable#Fragment_fragmentAllowReturnTransitionOverlap + */ + public boolean getAllowReturnTransitionOverlap() { + return (mAllowReturnTransitionOverlap == null) ? true : mAllowReturnTransitionOverlap; + } + /** * Print the Fragments's state into the given stream. * @@ -1588,53 +1908,53 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene */ public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { writer.print(prefix); writer.print("mFragmentId=#"); - writer.print(Integer.toHexString(mFragmentId)); - writer.print(" mContainerId=#"); - writer.print(Integer.toHexString(mContainerId)); - writer.print(" mTag="); writer.println(mTag); + writer.print(Integer.toHexString(mFragmentId)); + writer.print(" mContainerId=#"); + writer.print(Integer.toHexString(mContainerId)); + writer.print(" mTag="); writer.println(mTag); writer.print(prefix); writer.print("mState="); writer.print(mState); - writer.print(" mIndex="); writer.print(mIndex); - writer.print(" mWho="); writer.print(mWho); - writer.print(" mBackStackNesting="); writer.println(mBackStackNesting); + writer.print(" mIndex="); writer.print(mIndex); + writer.print(" mWho="); writer.print(mWho); + writer.print(" mBackStackNesting="); writer.println(mBackStackNesting); writer.print(prefix); writer.print("mAdded="); writer.print(mAdded); - writer.print(" mRemoving="); writer.print(mRemoving); - writer.print(" mResumed="); writer.print(mResumed); - writer.print(" mFromLayout="); writer.print(mFromLayout); - writer.print(" mInLayout="); writer.println(mInLayout); + writer.print(" mRemoving="); writer.print(mRemoving); + writer.print(" mResumed="); writer.print(mResumed); + writer.print(" mFromLayout="); writer.print(mFromLayout); + writer.print(" mInLayout="); writer.println(mInLayout); writer.print(prefix); writer.print("mHidden="); writer.print(mHidden); - writer.print(" mDetached="); writer.print(mDetached); - writer.print(" mMenuVisible="); writer.print(mMenuVisible); - writer.print(" mHasMenu="); writer.println(mHasMenu); + writer.print(" mDetached="); writer.print(mDetached); + writer.print(" mMenuVisible="); writer.print(mMenuVisible); + writer.print(" mHasMenu="); writer.println(mHasMenu); writer.print(prefix); writer.print("mRetainInstance="); writer.print(mRetainInstance); - writer.print(" mRetaining="); writer.print(mRetaining); - writer.print(" mUserVisibleHint="); writer.println(mUserVisibleHint); + writer.print(" mRetaining="); writer.print(mRetaining); + writer.print(" mUserVisibleHint="); writer.println(mUserVisibleHint); if (mFragmentManager != null) { writer.print(prefix); writer.print("mFragmentManager="); - writer.println(mFragmentManager); + writer.println(mFragmentManager); } if (mActivity != null) { writer.print(prefix); writer.print("mActivity="); - writer.println(mActivity); + writer.println(mActivity); } if (mParentFragment != null) { writer.print(prefix); writer.print("mParentFragment="); - writer.println(mParentFragment); + writer.println(mParentFragment); } if (mArguments != null) { writer.print(prefix); writer.print("mArguments="); writer.println(mArguments); } if (mSavedFragmentState != null) { writer.print(prefix); writer.print("mSavedFragmentState="); - writer.println(mSavedFragmentState); + writer.println(mSavedFragmentState); } if (mSavedViewState != null) { writer.print(prefix); writer.print("mSavedViewState="); - writer.println(mSavedViewState); + writer.println(mSavedViewState); } if (mTarget != null) { writer.print(prefix); writer.print("mTarget="); writer.print(mTarget); - writer.print(" mTargetRequestCode="); - writer.println(mTargetRequestCode); + writer.print(" mTargetRequestCode="); + writer.println(mTargetRequestCode); } if (mNextAnim != 0) { writer.print(prefix); writer.print("mNextAnim="); writer.println(mNextAnim); @@ -1648,7 +1968,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene if (mAnimatingAway != null) { writer.print(prefix); writer.print("mAnimatingAway="); writer.println(mAnimatingAway); writer.print(prefix); writer.print("mStateAfterAnimating="); - writer.println(mStateAfterAnimating); + writer.println(mStateAfterAnimating); } if (mLoaderManager != null) { writer.print(prefix); writer.println("Loader Manager:"); @@ -1886,7 +2206,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene throw new SuperNotCalledException("Fragment " + this + " did not call through to super.onStop()"); } - + if (mLoadersStarted) { mLoadersStarted = false; if (!mCheckedForLoaderManager) { @@ -1929,4 +2249,23 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene + " did not call through to super.onDestroy()"); } } + + private static Transition loadTransition(Context context, TypedArray typedArray, + Transition currentValue, Transition defaultValue, int id) { + if (currentValue != defaultValue) { + return currentValue; + } + int transitionId = typedArray.getResourceId(id, 0); + Transition transition = defaultValue; + if (transitionId != 0 && transitionId != com.android.internal.R.transition.no_transition) { + TransitionInflater inflater = TransitionInflater.from(context); + transition = inflater.inflateTransition(transitionId); + if (transition instanceof TransitionSet && + ((TransitionSet)transition).getTransitionCount() == 0) { + transition = null; + } + } + return transition; + } + } diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 1df1d42..ef69fdd 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -1497,7 +1497,10 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate return false; } final BackStackRecord bss = mBackStack.remove(last); - bss.popFromBackStack(true, null); + SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>(); + SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>(); + bss.calculateBackFragments(firstOutFragments, lastInFragments); + bss.popFromBackStack(true, null, firstOutFragments, lastInFragments); reportBackStackChanged(); } else { int index = -1; @@ -1541,10 +1544,16 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate states.add(mBackStack.remove(i)); } final int LAST = states.size()-1; + SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>(); + SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>(); + for (int i=0; i<=LAST; i++) { + states.get(i).calculateBackFragments(firstOutFragments, lastInFragments); + } BackStackRecord.TransitionState state = null; for (int i=0; i<=LAST; i++) { if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i)); - state = states.get(i).popFromBackStack(i == LAST, state); + state = states.get(i).popFromBackStack(i == LAST, state, + firstOutFragments, lastInFragments); } reportBackStackChanged(); } diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java index 1077bac..25cd3cc 100644 --- a/core/java/android/app/FragmentTransaction.java +++ b/core/java/android/app/FragmentTransaction.java @@ -172,19 +172,16 @@ public abstract class FragmentTransaction { public abstract FragmentTransaction setTransition(int transit); /** - * Set a {@link android.transition.Transition} resource id to use with this transaction. - * <var>transitionId</var> will be played for fragments when going forward and when popping - * the back stack. - * @param sceneRootId The ID of the element acting as the scene root for the transition. - * This should be a ViewGroup containing all Fragments in the transaction. - * @param transitionId The resource ID for the Transition used during the Fragment transaction. + * TODO: remove from API + * @hide */ - public abstract FragmentTransaction setCustomTransition(int sceneRootId, int transitionId); + public FragmentTransaction setCustomTransition(int sceneRootId, int transitionId) { + return this; + } /** - * Used with {@link #setCustomTransition(int, int)} to map a View from a removed or hidden - * Fragment to a View from a shown or added Fragment. - * <var>sharedElement</var> must have a unique transitionName in the View hierarchy. + * Used with to map a View from a removed or hidden Fragment to a View from a shown + * or added Fragment. * @param sharedElement A View in a disappearing Fragment to match with a View in an * appearing Fragment. * @param name The transitionName for a View in an appearing Fragment to match to the shared diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 214f50c..dbd180f 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -23,6 +23,7 @@ import android.content.ComponentName; import android.content.Intent; import android.content.pm.ParceledListSlice; import android.net.Uri; +import android.os.Bundle; import android.service.notification.Condition; import android.service.notification.IConditionListener; import android.service.notification.IConditionProvider; @@ -58,13 +59,15 @@ interface INotificationManager void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id); void cancelNotificationsFromListener(in INotificationListener token, in String[] keys); - ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys); + ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys, int trim); void requestHintsFromListener(in INotificationListener token, int hints); int getHintsFromListener(in INotificationListener token); void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter); int getInterruptionFilterFromListener(in INotificationListener token); + void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim); ComponentName getEffectsSuppressor(); + boolean matchesCallFilter(in Bundle extras); ZenModeConfig getZenModeConfig(); boolean setZenModeConfig(in ZenModeConfig config); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 8a26ba5..6cc6fb2 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -22,7 +22,6 @@ import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.PorterDuff; @@ -1424,6 +1423,9 @@ public class Notification implements Parcelable extras.remove(Notification.EXTRA_LARGE_ICON); extras.remove(Notification.EXTRA_LARGE_ICON_BIG); extras.remove(Notification.EXTRA_PICTURE); + extras.remove(Notification.EXTRA_BIG_TEXT); + // Prevent light notifications from being rebuilt. + extras.remove(Builder.EXTRA_NEEDS_REBUILD); } } @@ -2621,10 +2623,46 @@ public class Notification implements Parcelable contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize); } + private void unshrinkLine3Text(RemoteViews contentView) { + float regularTextSize = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.notification_text_size); + contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, regularTextSize); + } + + private void resetStandardTemplate(RemoteViews contentView) { + removeLargeIconBackground(contentView); + contentView.setViewPadding(R.id.icon, 0, 0, 0, 0); + contentView.setImageViewResource(R.id.icon, 0); + contentView.setInt(R.id.icon, "setBackgroundResource", 0); + contentView.setViewVisibility(R.id.right_icon, View.GONE); + contentView.setInt(R.id.right_icon, "setBackgroundResource", 0); + contentView.setImageViewResource(R.id.right_icon, 0); + contentView.setImageViewResource(R.id.icon, 0); + contentView.setTextViewText(R.id.title, null); + contentView.setTextViewText(R.id.text, null); + unshrinkLine3Text(contentView); + contentView.setTextViewText(R.id.text2, null); + contentView.setViewVisibility(R.id.text2, View.GONE); + contentView.setViewVisibility(R.id.info, View.GONE); + contentView.setViewVisibility(R.id.time, View.GONE); + contentView.setViewVisibility(R.id.line3, View.GONE); + contentView.setViewVisibility(R.id.overflow_divider, View.GONE); + contentView.setViewVisibility(R.id.progress, View.GONE); + } + private RemoteViews applyStandardTemplate(int resId) { + return applyStandardTemplate(resId, true /* hasProgress */); + } + + /** + * @param hasProgress whether the progress bar should be shown and set + */ + private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) { RemoteViews contentView = new BuilderRemoteViews(mContext.getPackageName(), mOriginatingUserId, resId); + resetStandardTemplate(contentView); + boolean showLine3 = false; boolean showLine2 = false; boolean contentTextInLine2 = false; @@ -2681,7 +2719,7 @@ public class Notification implements Parcelable } } else { contentView.setViewVisibility(R.id.text2, View.GONE); - if (mProgressMax != 0 || mProgressIndeterminate) { + if (hasProgress && (mProgressMax != 0 || mProgressIndeterminate)) { contentView.setProgressBar( R.id.progress, mProgressMax, mProgress, mProgressIndeterminate); contentView.setViewVisibility(R.id.progress, View.VISIBLE); @@ -2696,7 +2734,7 @@ public class Notification implements Parcelable shrinkLine3Text(contentView); } - if (mWhen != 0 && mShowWhen) { + if (showsTimeOrChronometer()) { if (mUseChronometer) { contentView.setViewVisibility(R.id.chronometer, View.VISIBLE); contentView.setLong(R.id.chronometer, "setBase", @@ -2730,6 +2768,14 @@ public class Notification implements Parcelable } /** + * @return true if the built notification will show the time or the chronometer; false + * otherwise + */ + private boolean showsTimeOrChronometer() { + return mWhen != 0 && mShowWhen; + } + + /** * Logic to find out whether the notification is going to have three lines in the contracted * layout. This is used to adjust the top padding. * @@ -2738,13 +2784,14 @@ public class Notification implements Parcelable */ private boolean hasThreeLines() { boolean contentTextInLine2 = mSubText != null && mContentText != null; - + boolean hasProgress = mStyle == null || mStyle.hasProgress(); // If we have content text in line 2, badge goes into line 2, or line 3 otherwise boolean badgeInLine3 = getProfileBadgeDrawable() != null && !contentTextInLine2; boolean hasLine3 = mContentText != null || mContentInfo != null || mNumber > 0 || badgeInLine3; boolean hasLine2 = (mSubText != null && mContentText != null) || - (mSubText == null && (mProgressMax != 0 || mProgressIndeterminate)); + (hasProgress && mSubText == null + && (mProgressMax != 0 || mProgressIndeterminate)); return hasLine2 && hasLine3; } @@ -2767,15 +2814,22 @@ public class Notification implements Parcelable return Math.round((1 - largeFactor) * padding + largeFactor * largePadding); } + private void resetStandardTemplateWithActions(RemoteViews big) { + big.setViewVisibility(R.id.actions, View.GONE); + big.setViewVisibility(R.id.action_divider, View.GONE); + big.removeAllViews(R.id.actions); + } + private RemoteViews applyStandardTemplateWithActions(int layoutId) { RemoteViews big = applyStandardTemplate(layoutId); + resetStandardTemplateWithActions(big); + int N = mActions.size(); if (N > 0) { big.setViewVisibility(R.id.actions, View.VISIBLE); big.setViewVisibility(R.id.action_divider, View.VISIBLE); if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS; - big.removeAllViews(R.id.actions); for (int i=0; i<N; i++) { final RemoteViews button = generateActionButton(mActions.get(i)); big.addView(R.id.actions, button); @@ -3486,6 +3540,15 @@ public class Notification implements Parcelable checkBuilder(); return mBuilder.build(); } + + /** + * @hide + * @return true if the style positions the progress bar on the second line; false if the + * style hides the progress bar + */ + protected boolean hasProgress() { + return true; + } } /** @@ -3883,7 +3946,7 @@ public class Notification implements Parcelable * @see Notification#bigContentView */ public static class MediaStyle extends Style { - static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 2; + static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3; static final int MAX_MEDIA_BUTTONS = 5; private int[] mActionsToShowInCompact = null; @@ -3897,8 +3960,10 @@ public class Notification implements Parcelable } /** - * Request up to 2 actions (by index in the order of addition) to be shown in the compact + * Request up to 3 actions (by index in the order of addition) to be shown in the compact * notification view. + * + * @param actions the indices of the actions to show in the compact notification view */ public MediaStyle setShowActionsInCompactView(int...actions) { mActionsToShowInCompact = actions; @@ -3975,6 +4040,9 @@ public class Notification implements Parcelable RemoteViews button = new RemoteViews(mBuilder.mContext.getPackageName(), R.layout.notification_material_media_action); button.setImageViewResource(R.id.action0, action.icon); + button.setDrawableParameters(R.id.action0, false, -1, + 0xFFFFFFFF, + PorterDuff.Mode.SRC_ATOP, -1); if (!tombstone) { button.setOnClickPendingIntent(R.id.action0, action.actionIntent); } @@ -3984,14 +4052,14 @@ public class Notification implements Parcelable private RemoteViews makeMediaContentView() { RemoteViews view = mBuilder.applyStandardTemplate( - R.layout.notification_template_material_media); + R.layout.notification_template_material_media, false /* hasProgress */); final int numActions = mBuilder.mActions.size(); final int N = mActionsToShowInCompact == null ? 0 : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT); if (N > 0) { - view.removeAllViews(R.id.actions); + view.removeAllViews(com.android.internal.R.id.media_actions); for (int i = 0; i < N; i++) { if (i >= numActions) { throw new IllegalArgumentException(String.format( @@ -4001,26 +4069,73 @@ public class Notification implements Parcelable final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]); final RemoteViews button = generateMediaActionButton(action); - view.addView(R.id.actions, button); + view.addView(com.android.internal.R.id.media_actions, button); } } + styleText(view); + hideRightIcon(view); return view; } private RemoteViews makeMediaBigContentView() { - RemoteViews big = mBuilder.applyStandardTemplate( - R.layout.notification_template_material_big_media); + final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS); + RemoteViews big = mBuilder.applyStandardTemplate(getBigLayoutResource(actionCount), + false /* hasProgress */); - final int N = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS); - if (N > 0) { - big.removeAllViews(R.id.actions); - for (int i=0; i<N; i++) { + if (actionCount > 0) { + big.removeAllViews(com.android.internal.R.id.media_actions); + for (int i = 0; i < actionCount; i++) { final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i)); - big.addView(R.id.actions, button); + big.addView(com.android.internal.R.id.media_actions, button); } } + styleText(big); + hideRightIcon(big); + applyTopPadding(big); + big.setViewVisibility(android.R.id.progress, View.GONE); return big; } + + private int getBigLayoutResource(int actionCount) { + if (actionCount <= 3) { + return R.layout.notification_template_material_big_media_narrow; + } else { + return R.layout.notification_template_material_big_media; + } + } + + private void hideRightIcon(RemoteViews contentView) { + contentView.setViewVisibility(R.id.right_icon, View.GONE); + } + + /** + * Applies the special text colors for media notifications to all text views. + */ + private void styleText(RemoteViews contentView) { + int primaryColor = mBuilder.mContext.getResources().getColor( + R.color.notification_media_primary_color); + int secondaryColor = mBuilder.mContext.getResources().getColor( + R.color.notification_media_secondary_color); + contentView.setTextColor(R.id.title, primaryColor); + if (mBuilder.showsTimeOrChronometer()) { + if (mBuilder.mUseChronometer) { + contentView.setTextColor(R.id.chronometer, secondaryColor); + } else { + contentView.setTextColor(R.id.time, secondaryColor); + } + } + contentView.setTextColor(R.id.text2, secondaryColor); + contentView.setTextColor(R.id.text, secondaryColor); + contentView.setTextColor(R.id.info, secondaryColor); + } + + /** + * @hide + */ + @Override + protected boolean hasProgress() { + return false; + } } // When adding a new Style subclass here, don't forget to update diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index fc047de..7dc1ad6 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -20,6 +20,7 @@ import android.annotation.SdkConstant; import android.app.Notification.Builder; import android.content.ComponentName; import android.content.Context; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; @@ -251,5 +252,17 @@ public class NotificationManager } } + /** + * @hide + */ + public boolean matchesCallFilter(Bundle extras) { + INotificationManager service = getService(); + try { + return service.matchesCallFilter(extras); + } catch (RemoteException e) { + return false; + } + } + private Context mContext; } diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java index dcdfd78..723cb9b 100644 --- a/core/java/android/app/VoiceInteractor.java +++ b/core/java/android/app/VoiceInteractor.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.SystemApi; import android.content.Context; import android.os.Bundle; import android.os.IBinder; @@ -33,6 +34,7 @@ import com.android.internal.os.SomeArgs; import java.util.ArrayList; /** + * @hide * Interface for an {@link Activity} to interact with the user through voice. Use * {@link android.app.Activity#getVoiceInteractor() Activity.getVoiceInteractor} * to retrieve the interface, if the activity is currently involved in a voice interaction. @@ -54,6 +56,7 @@ import java.util.ArrayList; * request, rather than holding on to the activity instance yourself, either explicitly * or implicitly through a non-static inner class. */ +@SystemApi public class VoiceInteractor { static final String TAG = "VoiceInteractor"; static final boolean DEBUG = true; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 69b1139..9ed8960 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -115,6 +115,18 @@ public class DevicePolicyManager { = "android.app.action.ACTION_PROVISION_MANAGED_PROFILE"; /** + * A {@link Parcelable} extra of type {@link PersistableBundle} that allows a mobile device + * management application that starts managed profile provisioning to pass data to itself on the + * managed profile when provisioning completes. The mobile device management application sends + * this extra in an intent with the action {@link #ACTION_PROVISION_MANAGED_PROFILE} and + * receives it in {@link DeviceAdminReceiver#onProfileProvisioningComplete} via an intent with + * the action {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE}. The bundle is + * not changed during the managed profile provisioning. + */ + public static final String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = + "android.app.extra.ADMIN_EXTRA_BUNDLE"; + + /** * A String extra holding the package name of the mobile device management application that * will be set as the profile owner or device owner. * diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index 2431ad0..fb80de2 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -106,7 +106,9 @@ public final class UsageEvents implements Parcelable { } /** - * The time at which this event occurred. + * The time at which this event occurred, measured in milliseconds since the epoch. + * <p/> + * See {@link System#currentTimeMillis()}. */ public long getTimeStamp() { return mTimeStamp; diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java index e47a802..abfc435 100644 --- a/core/java/android/app/usage/UsageStats.java +++ b/core/java/android/app/usage/UsageStats.java @@ -81,28 +81,36 @@ public final class UsageStats implements Parcelable { } /** - * Get the beginning of the time range this {@link android.app.usage.UsageStats} represents. + * Get the beginning of the time range this {@link android.app.usage.UsageStats} represents, + * measured in milliseconds since the epoch. + * <p/> + * See {@link System#currentTimeMillis()}. */ public long getFirstTimeStamp() { return mBeginTimeStamp; } /** - * Get the end of the time range this {@link android.app.usage.UsageStats} represents. + * Get the end of the time range this {@link android.app.usage.UsageStats} represents, + * measured in milliseconds since the epoch. + * <p/> + * See {@link System#currentTimeMillis()}. */ public long getLastTimeStamp() { return mEndTimeStamp; } /** - * Get the last time this package was used. + * Get the last time this package was used, measured in milliseconds since the epoch. + * <p/> + * See {@link System#currentTimeMillis()}. */ public long getLastTimeUsed() { return mLastTimeUsed; } /** - * Get the total time this package spent in the foreground. + * Get the total time this package spent in the foreground, measured in milliseconds. */ public long getTotalTimeInForeground() { return mTotalTimeInForeground; diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index f9b8928..5830fcf 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -23,6 +23,7 @@ import android.util.ArrayMap; import java.util.Collections; import java.util.List; +import java.util.Map; /** * Provides access to device usage history and statistics. Usage data is aggregated into @@ -149,7 +150,6 @@ public final class UsageStatsManager { * @param endTime The exclusive end of the range of events to include in the results. * @return A {@link UsageEvents}. */ - @SuppressWarnings("unchecked") public UsageEvents queryEvents(long beginTime, long endTime) { try { UsageEvents iter = mService.queryEvents(beginTime, endTime, @@ -170,15 +170,13 @@ public final class UsageStatsManager { * * @param beginTime The inclusive beginning of the range of stats to include in the results. * @param endTime The exclusive end of the range of stats to include in the results. - * @return An {@link android.util.ArrayMap} keyed by package name or null if no stats are + * @return A {@link java.util.Map} keyed by package name, or null if no stats are * available. */ - public ArrayMap<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) { + public Map<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) { List<UsageStats> stats = queryUsageStats(INTERVAL_BEST, beginTime, endTime); if (stats.isEmpty()) { - @SuppressWarnings("unchecked") - ArrayMap<String, UsageStats> emptyStats = ArrayMap.EMPTY; - return emptyStats; + return Collections.emptyMap(); } ArrayMap<String, UsageStats> aggregatedStats = new ArrayMap<>(); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index b7d7c25..f979a0c 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1486,8 +1486,6 @@ public abstract class Context { * @see #sendBroadcast(Intent) * @see #sendBroadcast(Intent, String) * @see #sendOrderedBroadcast(Intent, String) - * @see #sendStickyBroadcast(Intent) - * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) * @see android.content.BroadcastReceiver * @see #registerReceiver * @see android.app.Activity#RESULT_OK @@ -1584,7 +1582,7 @@ public abstract class Context { @Nullable Bundle initialExtras); /** - * Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the + * <p>Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the * Intent you are sending stays around after the broadcast is complete, * so that others can quickly retrieve that data through the return * value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}. In @@ -1595,6 +1593,12 @@ public abstract class Context { * permission in order to use this API. If you do not hold that * permission, {@link SecurityException} will be thrown. * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em> + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast, and the Intent will be held to * be re-broadcast to future receivers. @@ -1602,10 +1606,11 @@ public abstract class Context { * @see #sendBroadcast(Intent) * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) */ + @Deprecated public abstract void sendStickyBroadcast(Intent intent); /** - * Version of {@link #sendStickyBroadcast} that allows you to + * <p>Version of {@link #sendStickyBroadcast} that allows you to * receive data back from the broadcast. This is accomplished by * supplying your own BroadcastReceiver when calling, which will be * treated as a final receiver at the end of the broadcast -- its @@ -1622,6 +1627,12 @@ public abstract class Context { * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em> + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast. * @param resultReceiver Your own BroadcastReceiver to treat as the final @@ -1644,31 +1655,45 @@ public abstract class Context { * @see #registerReceiver * @see android.app.Activity#RESULT_OK */ + @Deprecated public abstract void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, @Nullable Handler scheduler, int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras); /** - * Remove the data previously sent with {@link #sendStickyBroadcast}, + * <p>Remove the data previously sent with {@link #sendStickyBroadcast}, * so that it is as if the sticky broadcast had never happened. * * <p>You must hold the {@link android.Manifest.permission#BROADCAST_STICKY} * permission in order to use this API. If you do not hold that * permission, {@link SecurityException} will be thrown. * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em> + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * * @param intent The Intent that was previously broadcast. * * @see #sendStickyBroadcast */ + @Deprecated public abstract void removeStickyBroadcast(Intent intent); /** - * Version of {@link #sendStickyBroadcast(Intent)} that allows you to specify the + * <p>Version of {@link #sendStickyBroadcast(Intent)} that allows you to specify the * user the broadcast will be sent to. This is not available to applications * that are not pre-installed on the system image. Using it requires holding * the INTERACT_ACROSS_USERS permission. * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em> + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast, and the Intent will be held to * be re-broadcast to future receivers. @@ -1676,10 +1701,11 @@ public abstract class Context { * * @see #sendBroadcast(Intent) */ + @Deprecated public abstract void sendStickyBroadcastAsUser(Intent intent, UserHandle user); /** - * Version of + * <p>Version of * {@link #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)} * that allows you to specify the * user the broadcast will be sent to. This is not available to applications @@ -1688,6 +1714,12 @@ public abstract class Context { * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em> + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast. * @param user UserHandle to send the intent to. @@ -1705,13 +1737,14 @@ public abstract class Context { * * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) */ + @Deprecated public abstract void sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle user, BroadcastReceiver resultReceiver, @Nullable Handler scheduler, int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras); /** - * Version of {@link #removeStickyBroadcast(Intent)} that allows you to specify the + * <p>Version of {@link #removeStickyBroadcast(Intent)} that allows you to specify the * user the broadcast will be sent to. This is not available to applications * that are not pre-installed on the system image. Using it requires holding * the INTERACT_ACROSS_USERS permission. @@ -1720,11 +1753,18 @@ public abstract class Context { * permission in order to use this API. If you do not hold that * permission, {@link SecurityException} will be thrown. * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em> + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * * @param intent The Intent that was previously broadcast. * @param user UserHandle to remove the sticky broadcast from. * * @see #sendStickyBroadcastAsUser */ + @Deprecated public abstract void removeStickyBroadcastAsUser(Intent intent, UserHandle user); /** diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 61e105b..b825c94 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2216,104 +2216,14 @@ public class Intent implements Parcelable, Cloneable { /** * Broadcast Action: Wired Headset plugged in or unplugged. * - * You <em>cannot</em> receive this through components declared - * in manifests, only by explicitly registering for it with - * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter) - * Context.registerReceiver()}. - * - * <p>The intent will have the following extra values: - * <ul> - * <li><em>state</em> - 0 for unplugged, 1 for plugged. </li> - * <li><em>name</em> - Headset type, human readable string </li> - * <li><em>microphone</em> - 1 if headset has a microphone, 0 otherwise </li> - * </ul> - * </ul> - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_HEADSET_PLUG = - "android.intent.action.HEADSET_PLUG"; - - /** - * Broadcast Action: An analog audio speaker/headset plugged in or unplugged. - * - * <p>The intent will have the following extra values: - * <ul> - * <li><em>state</em> - 0 for unplugged, 1 for plugged. </li> - * <li><em>name</em> - Headset type, human readable string </li> - * </ul> - * </ul> - * @hide - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_ANALOG_AUDIO_DOCK_PLUG = - "android.intent.action.ANALOG_AUDIO_DOCK_PLUG"; - - /** - * Broadcast Action: A digital audio speaker/headset plugged in or unplugged. - * - * <p>The intent will have the following extra values: - * <ul> - * <li><em>state</em> - 0 for unplugged, 1 for plugged. </li> - * <li><em>name</em> - Headset type, human readable string </li> - * </ul> - * </ul> - * @hide - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_DIGITAL_AUDIO_DOCK_PLUG = - "android.intent.action.DIGITAL_AUDIO_DOCK_PLUG"; - - /** - * Broadcast Action: A sticky broadcast indicating an HMDI cable was plugged or unplugged - * - * <p>The intent will have the following extra values: - * <ul> - * <li><em>state</em> - 0 for unplugged, 1 for plugged. </li> - * <li><em>name</em> - HDMI cable, human readable string </li> - * <li><em>maxChannelCount</em> - the maximum number of output channels supported by the - * connected HDMI device, only available when <i>state</i> is 1.</li> - * <li><em>encodings</em> - an array of formats supported by the connected HDMI device, - * only available when <i>state</i> is 1. Encoding values are defined in - * {@link android.media.AudioFormat} (for instance see - * {@link android.media.AudioFormat#ENCODING_PCM_16BIT}). Use - * {@link #getIntArrayExtra(String)} to retrieve the encoding values.</li> - * </ul> + * Same as {@link android.media.AudioManager#ACTION_HEADSET_PLUG}, to be consulted for value + * and documentation. + * <p>If the minimum SDK version of your application is + * {@link android.os.Build.VERSION_CODES#L}, it is recommended to refer + * to the <code>AudioManager</code> constant in your receiver registration code instead. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_HDMI_AUDIO_PLUG = - "android.intent.action.HDMI_AUDIO_PLUG"; - - /** - * Broadcast Action: A USB audio accessory was plugged in or unplugged. - * - * <p>The intent will have the following extra values: - * <ul> - * <li><em>state</em> - 0 for unplugged, 1 for plugged. </li> - * <li><em>card</em> - ALSA card number (integer) </li> - * <li><em>device</em> - ALSA device number (integer) </li> - * </ul> - * </ul> - * @hide - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_USB_AUDIO_ACCESSORY_PLUG = - "android.intent.action.USB_AUDIO_ACCESSORY_PLUG"; - - /** - * Broadcast Action: A USB audio device was plugged in or unplugged. - * - * <p>The intent will have the following extra values: - * <ul> - * <li><em>state</em> - 0 for unplugged, 1 for plugged. </li> - * <li><em>card</em> - ALSA card number (integer) </li> - * <li><em>device</em> - ALSA device number (integer) </li> - * </ul> - * </ul> - * @hide - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_USB_AUDIO_DEVICE_PLUG = - "android.intent.action.USB_AUDIO_DEVICE_PLUG"; + public static final String ACTION_HEADSET_PLUG = android.media.AudioManager.ACTION_HEADSET_PLUG; /** * <p>Broadcast Action: The user has switched on advanced settings in the settings app:</p> @@ -2833,12 +2743,14 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_BROWSABLE = "android.intent.category.BROWSABLE"; /** + * @hide * Categories for activities that can participate in voice interaction. * An activity that supports this category must be prepared to run with * no UI shown at all (though in some case it may have a UI shown), and * rely on {@link android.app.VoiceInteractor} to interact with the user. */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) + @SystemApi public static final String CATEGORY_VOICE = "android.intent.category.VOICE"; /** * Set if the activity should be considered as an alternative action to diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 44478d4..3e1f60a 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -32,6 +32,7 @@ import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; +import android.content.pm.KeySet; import android.content.pm.PackageInfo; import android.content.pm.ManifestDigest; import android.content.pm.PackageCleanItem; @@ -198,6 +199,14 @@ interface IPackageManager { in VerificationParams verificationParams, in String packageAbiOverride); + void installPackageAsUser(in String originPath, + in IPackageInstallObserver2 observer, + int flags, + in String installerPackageName, + in VerificationParams verificationParams, + in String packageAbiOverride, + int userId); + void finishPackageInstall(int token); void setInstallerPackageName(in String targetPackage, in String installerPackageName); @@ -446,8 +455,8 @@ interface IPackageManager { boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId); boolean getBlockUninstallForUser(String packageName, int userId); - IBinder getKeySetByAlias(String packageName, String alias); - IBinder getSigningKeySet(String packageName); - boolean isPackageSignedByKeySet(String packageName, IBinder ks); - boolean isPackageSignedByKeySetExactly(String packageName, IBinder ks); + KeySet getKeySetByAlias(String packageName, String alias); + KeySet getSigningKeySet(String packageName); + boolean isPackageSignedByKeySet(String packageName, in KeySet ks); + boolean isPackageSignedByKeySetExactly(String packageName, in KeySet ks); } diff --git a/core/java/android/content/pm/KeySet.aidl b/core/java/android/content/pm/KeySet.aidl new file mode 100644 index 0000000..493d288 --- /dev/null +++ b/core/java/android/content/pm/KeySet.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2014, 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.content.pm; + +parcelable KeySet;
\ No newline at end of file diff --git a/core/java/android/content/pm/KeySet.java b/core/java/android/content/pm/KeySet.java index fcdaa18..643db7e 100644 --- a/core/java/android/content/pm/KeySet.java +++ b/core/java/android/content/pm/KeySet.java @@ -17,13 +17,16 @@ package android.content.pm; import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; /** * Represents a {@code KeySet} that has been declared in the AndroidManifest.xml * file for the application. A {@code KeySet} can be used explicitly to * represent a trust relationship with other applications on the device. + * @hide */ -public class KeySet { +public class KeySet implements Parcelable { private IBinder token; @@ -40,6 +43,7 @@ public class KeySet { return token; } + /** @hide */ @Override public boolean equals(Object o) { if (o instanceof KeySet) { @@ -48,4 +52,58 @@ public class KeySet { } return false; } + + /** @hide */ + @Override + public int hashCode() { + return token.hashCode(); + } + + /** + * Implement Parcelable + * @hide + */ + public static final Parcelable.Creator<KeySet> CREATOR + = new Parcelable.Creator<KeySet>() { + + /** + * Create a KeySet from a Parcel + * + * @param in The parcel containing the KeySet + */ + public KeySet createFromParcel(Parcel source) { + return readFromParcel(source); + } + + /** + * Create an array of null KeySets + */ + public KeySet[] newArray(int size) { + return new KeySet[size]; + } + }; + + /** + * @hide + */ + private static KeySet readFromParcel(Parcel in) { + IBinder token = in.readStrongBinder(); + return new KeySet(token); + } + + /** + * @hide + */ + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeStrongBinder(token); + } + + /** + * @hide + */ + @Override + public int describeContents() { + return 0; + } }
\ No newline at end of file diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index fa2bb4d..1b15ff5 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3698,10 +3698,13 @@ public abstract class PackageManager { * * @param alias The alias for a given {@link KeySet} as defined in the * application's AndroidManifest.xml. + * @hide */ public abstract KeySet getKeySetByAlias(String packageName, String alias); - /** Return the signing {@link KeySet} for this application. */ + /** Return the signing {@link KeySet} for this application. + * @hide + */ public abstract KeySet getSigningKeySet(String packageName); /** @@ -3709,6 +3712,7 @@ public abstract class PackageManager { * of the keys specified by the {@link KeySet} ks. This will return true if * the package has been signed by additional keys (a superset) as well. * Compare to {@link #isSignedByExactly(String packageName, KeySet ks)}. + * @hide */ public abstract boolean isSignedBy(String packageName, KeySet ks); @@ -3716,6 +3720,7 @@ public abstract class PackageManager { * Return whether the package denoted by packageName has been signed by all * of, and only, the keys specified by the {@link KeySet} ks. Compare to * {@link #isSignedBy(String packageName, KeySet ks)}. + * @hide */ public abstract boolean isSignedByExactly(String packageName, KeySet ks); diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 3c9b7b3..a57b361 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -1575,6 +1575,13 @@ public abstract class CameraMetadata<TKey> { */ public static final int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17; + /** + * <p>Turn on custom high dynamic range (HDR) mode.</p> + * @see CaptureRequest#CONTROL_SCENE_MODE + * @hide + */ + public static final int CONTROL_SCENE_MODE_HDR = 18; + // // Enumeration values for CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE // diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java index ee0ca9c..a8d1018 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java +++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java @@ -882,6 +882,7 @@ public class LegacyMetadataMapper { Parameters.SCENE_MODE_PARTY, Parameters.SCENE_MODE_CANDLELIGHT, Parameters.SCENE_MODE_BARCODE, + Parameters.SCENE_MODE_HDR, }; private final static int[] sSceneModes = { @@ -901,6 +902,7 @@ public class LegacyMetadataMapper { CameraCharacteristics.CONTROL_SCENE_MODE_PARTY, CameraCharacteristics.CONTROL_SCENE_MODE_CANDLELIGHT, CameraCharacteristics.CONTROL_SCENE_MODE_BARCODE, + CameraCharacteristics.CONTROL_SCENE_MODE_HDR, }; static int convertSceneModeFromLegacy(String mode) { diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 51b7229..b077e06 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -472,7 +472,7 @@ public final class DisplayManager { /** * Creates a virtual display. * - * @see #createVirtualDisplay(String, int, int, int, Surface, int, VirtualDisplay.Callbacks) + * @see #createVirtualDisplay(String, int, int, int, Surface, int, VirtualDisplay.Callback) */ public VirtualDisplay createVirtualDisplay(@NonNull String name, int width, int height, int densityDpi, @Nullable Surface surface, int flags) { @@ -513,7 +513,7 @@ public final class DisplayManager { * {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}, {@link #VIRTUAL_DISPLAY_FLAG_PRESENTATION}, * {@link #VIRTUAL_DISPLAY_FLAG_SECURE}, {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}, * or {@link #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}. - * @param callbacks Callbacks to call when the state of the {@link VirtualDisplay} changes + * @param callback Callback to call when the state of the {@link VirtualDisplay} changes * @param handler The handler on which the listener should be invoked, or null * if the listener should be invoked on the calling thread's looper. * @return The newly created virtual display, or null if the application could @@ -524,17 +524,17 @@ public final class DisplayManager { */ public VirtualDisplay createVirtualDisplay(@NonNull String name, int width, int height, int densityDpi, @Nullable Surface surface, int flags, - @Nullable VirtualDisplay.Callbacks callbacks, @Nullable Handler handler) { + @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) { return createVirtualDisplay(null, - name, width, height, densityDpi, surface, flags, callbacks, handler); + name, width, height, densityDpi, surface, flags, callback, handler); } /** @hide */ public VirtualDisplay createVirtualDisplay(@Nullable MediaProjection projection, @NonNull String name, int width, int height, int densityDpi, @Nullable Surface surface, - int flags, @Nullable VirtualDisplay.Callbacks callbacks, @Nullable Handler handler) { + int flags, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) { return mGlobal.createVirtualDisplay(mContext, projection, - name, width, height, densityDpi, surface, flags, callbacks, handler); + name, width, height, densityDpi, surface, flags, callback, handler); } /** diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 8b44f3b..0051ef5 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -372,7 +372,7 @@ public final class DisplayManagerGlobal { public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection, String name, int width, int height, int densityDpi, Surface surface, int flags, - VirtualDisplay.Callbacks callbacks, Handler handler) { + VirtualDisplay.Callback callback, Handler handler) { if (TextUtils.isEmpty(name)) { throw new IllegalArgumentException("name must be non-null and non-empty"); } @@ -381,7 +381,7 @@ public final class DisplayManagerGlobal { + "greater than 0"); } - VirtualDisplayCallbacks callbackWrapper = new VirtualDisplayCallbacks(callbacks, handler); + VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler); IMediaProjection projectionToken = projection != null ? projection.getProjection() : null; int displayId; try { @@ -408,7 +408,7 @@ public final class DisplayManagerGlobal { return new VirtualDisplay(this, display, callbackWrapper, surface); } - public void setVirtualDisplaySurface(IVirtualDisplayCallbacks token, Surface surface) { + public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) { try { mDm.setVirtualDisplaySurface(token, surface); } catch (RemoteException ex) { @@ -416,7 +416,7 @@ public final class DisplayManagerGlobal { } } - public void resizeVirtualDisplay(IVirtualDisplayCallbacks token, + public void resizeVirtualDisplay(IVirtualDisplayCallback token, int width, int height, int densityDpi) { try { mDm.resizeVirtualDisplay(token, width, height, densityDpi); @@ -425,7 +425,7 @@ public final class DisplayManagerGlobal { } } - public void releaseVirtualDisplay(IVirtualDisplayCallbacks token) { + public void releaseVirtualDisplay(IVirtualDisplayCallback token) { try { mDm.releaseVirtualDisplay(token); } catch (RemoteException ex) { @@ -476,61 +476,61 @@ public final class DisplayManagerGlobal { } } - private final static class VirtualDisplayCallbacks extends IVirtualDisplayCallbacks.Stub { - private VirtualDisplayCallbacksDelegate mDelegate; + private final static class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub { + private VirtualDisplayCallbackDelegate mDelegate; - public VirtualDisplayCallbacks(VirtualDisplay.Callbacks callbacks, Handler handler) { - if (callbacks != null) { - mDelegate = new VirtualDisplayCallbacksDelegate(callbacks, handler); + public VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler) { + if (callback != null) { + mDelegate = new VirtualDisplayCallbackDelegate(callback, handler); } } @Override // Binder call - public void onDisplayPaused() { + public void onPaused() { if (mDelegate != null) { - mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_PAUSED); + mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_PAUSED); } } @Override // Binder call - public void onDisplayResumed() { + public void onResumed() { if (mDelegate != null) { - mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_RESUMED); + mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_RESUMED); } } @Override // Binder call - public void onDisplayStopped() { + public void onStopped() { if (mDelegate != null) { - mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_STOPPED); + mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_STOPPED); } } } - private final static class VirtualDisplayCallbacksDelegate extends Handler { + private final static class VirtualDisplayCallbackDelegate extends Handler { public static final int MSG_DISPLAY_PAUSED = 0; public static final int MSG_DISPLAY_RESUMED = 1; public static final int MSG_DISPLAY_STOPPED = 2; - private final VirtualDisplay.Callbacks mCallbacks; + private final VirtualDisplay.Callback mCallback; - public VirtualDisplayCallbacksDelegate(VirtualDisplay.Callbacks callbacks, + public VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback, Handler handler) { super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/); - mCallbacks = callbacks; + mCallback = callback; } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_DISPLAY_PAUSED: - mCallbacks.onDisplayPaused(); + mCallback.onPaused(); break; case MSG_DISPLAY_RESUMED: - mCallbacks.onDisplayResumed(); + mCallback.onResumed(); break; case MSG_DISPLAY_STOPPED: - mCallbacks.onDisplayStopped(); + mCallback.onStopped(); break; } } diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index cfaa5a0..4486dd4 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -17,7 +17,7 @@ package android.hardware.display; import android.hardware.display.IDisplayManagerCallback; -import android.hardware.display.IVirtualDisplayCallbacks; +import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.WifiDisplay; import android.hardware.display.WifiDisplayStatus; import android.media.projection.IMediaProjection; @@ -61,17 +61,17 @@ interface IDisplayManager { // Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate // MediaProjection token for certain combinations of flags. - int createVirtualDisplay(in IVirtualDisplayCallbacks callbacks, + int createVirtualDisplay(in IVirtualDisplayCallback callback, in IMediaProjection projectionToken, String packageName, String name, int width, int height, int densityDpi, in Surface surface, int flags); // No permissions required, but must be same Uid as the creator. - void resizeVirtualDisplay(in IVirtualDisplayCallbacks token, + void resizeVirtualDisplay(in IVirtualDisplayCallback token, int width, int height, int densityDpi); // No permissions required but must be same Uid as the creator. - void setVirtualDisplaySurface(in IVirtualDisplayCallbacks token, in Surface surface); + void setVirtualDisplaySurface(in IVirtualDisplayCallback token, in Surface surface); // No permissions required but must be same Uid as the creator. - void releaseVirtualDisplay(in IVirtualDisplayCallbacks token); + void releaseVirtualDisplay(in IVirtualDisplayCallback token); } diff --git a/core/java/android/hardware/display/IVirtualDisplayCallbacks.aidl b/core/java/android/hardware/display/IVirtualDisplayCallback.aidl index a1cdc01..c3490d1 100644 --- a/core/java/android/hardware/display/IVirtualDisplayCallbacks.aidl +++ b/core/java/android/hardware/display/IVirtualDisplayCallback.aidl @@ -16,20 +16,20 @@ package android.hardware.display; /** @hide */ -oneway interface IVirtualDisplayCallbacks { +oneway interface IVirtualDisplayCallback { /** * Called when the virtual display video projection has been * paused by the system or when the surface has been detached * by the application by calling setSurface(null). * The surface will not receive any more buffers while paused. */ - void onDisplayPaused(); + void onPaused(); /** * Called when the virtual display video projection has been * resumed after having been paused. */ - void onDisplayResumed(); + void onResumed(); /** * Called when the virtual display video projection has been @@ -37,5 +37,5 @@ oneway interface IVirtualDisplayCallbacks { * and it will never be resumed. It is still the responsibility * of the application to release() the virtual display. */ - void onDisplayStopped(); + void onStopped(); } diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java index 1dd6978..4ddf10f 100644 --- a/core/java/android/hardware/display/VirtualDisplay.java +++ b/core/java/android/hardware/display/VirtualDisplay.java @@ -35,11 +35,11 @@ import android.view.Surface; public final class VirtualDisplay { private final DisplayManagerGlobal mGlobal; private final Display mDisplay; - private IVirtualDisplayCallbacks mToken; + private IVirtualDisplayCallback mToken; private Surface mSurface; VirtualDisplay(DisplayManagerGlobal global, Display display, - IVirtualDisplayCallbacks token, Surface surface) { + IVirtualDisplayCallback token, Surface surface) { mGlobal = global; mDisplay = display; mToken = token; @@ -114,20 +114,20 @@ public final class VirtualDisplay { /** * Interface for receiving information about a {@link VirtualDisplay}'s state changes. */ - public static abstract class Callbacks { + public static abstract class Callback { /** * Called when the virtual display video projection has been * paused by the system or when the surface has been detached * by the application by calling setSurface(null). * The surface will not receive any more buffers while paused. */ - public void onDisplayPaused() { } + public void onPaused() { } /** * Called when the virtual display video projection has been * resumed after having been paused. */ - public void onDisplayResumed() { } + public void onResumed() { } /** * Called when the virtual display video projection has been @@ -135,6 +135,6 @@ public final class VirtualDisplay { * and it will never be resumed. It is still the responsibility * of the application to release() the virtual display. */ - public void onDisplayStopped() { } + public void onStopped() { } } } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 4bfef41..3c219fd 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -685,6 +685,23 @@ public class ConnectivityManager { } /** + * Returns the {@link Network} object currently serving a given type, or + * null if the given type is not connected. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}. + * + * @hide + */ + public Network getNetworkForType(int networkType) { + try { + return mService.getNetworkForType(networkType); + } catch (RemoteException e) { + return null; + } + } + + /** * Returns an array of all {@link Network} currently tracked by the * framework. * diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index b2fc3be..974c4cd 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -48,6 +48,7 @@ interface IConnectivityManager NetworkInfo getNetworkInfo(int networkType); NetworkInfo getNetworkInfoForNetwork(in Network network); NetworkInfo[] getAllNetworkInfo(); + Network getNetworkForType(int networkType); Network[] getAllNetworks(); NetworkInfo getProvisioningOrActiveNetworkInfo(); diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java index d2a4728..e686be7 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -35,6 +35,7 @@ import java.net.URLStreamHandler; import java.util.concurrent.atomic.AtomicReference; import javax.net.SocketFactory; +import com.android.okhttp.ConnectionPool; import com.android.okhttp.HostResolver; import com.android.okhttp.OkHttpClient; @@ -60,6 +61,17 @@ public class Network implements Parcelable { private volatile OkHttpClient mOkHttpClient = null; private Object mLock = new Object(); + // Default connection pool values. These are evaluated at startup, just + // like the OkHttp code. Also like the OkHttp code, we will throw parse + // exceptions at class loading time if the properties are set but are not + // valid integers. + private static final boolean httpKeepAlive = + Boolean.parseBoolean(System.getProperty("http.keepAlive", "true")); + private static final int httpMaxConnections = + httpKeepAlive ? Integer.parseInt(System.getProperty("http.maxConnections", "5")) : 0; + private static final long httpKeepAliveDurationMs = + Long.parseLong(System.getProperty("http.keepAliveDuration", "300000")); // 5 minutes. + /** * @hide */ @@ -183,6 +195,20 @@ public class Network implements Parcelable { return mNetworkBoundSocketFactory; } + // TODO: This creates an OkHttpClient with its own connection pool for + // every Network object, instead of one for every NetId. This is + // suboptimal, because an app could potentially have more than one + // Network object for the same NetId, causing increased memory footprint + // and performance penalties due to lack of connection reuse (connection + // setup time, congestion window growth time, etc.). + // + // Instead, investigate only having one OkHttpClient for every NetId, + // perhaps by using a static HashMap of NetIds to OkHttpClient objects. The + // tricky part is deciding when to remove an OkHttpClient; a WeakHashMap + // shouldn't be used because whether a Network is referenced doesn't + // correlate with whether a new Network will be instantiated in the near + // future with the same NetID. A good solution would involve purging empty + // (or when all connections are timed out) ConnectionPools. private void maybeInitHttpClient() { if (mOkHttpClient == null) { synchronized (mLock) { @@ -193,9 +219,12 @@ public class Network implements Parcelable { return Network.this.getAllByName(host); } }; + ConnectionPool pool = new ConnectionPool(httpMaxConnections, + httpKeepAliveDurationMs); mOkHttpClient = new OkHttpClient() .setSocketFactory(getSocketFactory()) - .setHostResolver(hostResolver); + .setHostResolver(hostResolver) + .setConnectionPool(pool); } } } diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java index d279412..7664c95 100644 --- a/core/java/android/net/NetworkInfo.java +++ b/core/java/android/net/NetworkInfo.java @@ -128,14 +128,6 @@ public class NetworkInfo implements Parcelable { private boolean mIsAvailable; /** - * @param type network type - * @deprecated - * @hide because this constructor was only meant for internal use (and - * has now been superseded by the package-private constructor below). - */ - public NetworkInfo(int type) {} - - /** * @hide */ public NetworkInfo(int type, int subtype, String typeName, String subtypeName) { diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 6d4a302..b3e28ea 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -19,6 +19,7 @@ package android.os; import android.net.InterfaceConfiguration; import android.net.INetworkManagementEventObserver; +import android.net.Network; import android.net.NetworkStats; import android.net.RouteInfo; import android.net.UidRange; @@ -164,10 +165,10 @@ interface INetworkManagementService /** * Sets the list of DNS forwarders (in order of priority) */ - void setDnsForwarders(in String[] dns); + void setDnsForwarders(in Network network, in String[] dns); /** - * Returns the list of DNS fowarders (in order of priority) + * Returns the list of DNS forwarders (in order of priority) */ String[] getDnsForwarders(); diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 713fcd8..3286627 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -52,4 +52,5 @@ interface IUserManager { void removeRestrictions(); void setDefaultGuestRestrictions(in Bundle restrictions); Bundle getDefaultGuestRestrictions(); + boolean markGuestForDeletion(int userHandle); } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index e215669..f793667 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -676,6 +676,22 @@ public class UserManager { } /** + * @hide + * Marks the guest user for deletion to allow a new guest to be created before deleting + * the current user who is a guest. + * @param userHandle + * @return + */ + public boolean markGuestForDeletion(int userHandle) { + try { + return mService.markGuestForDeletion(userHandle); + } catch (RemoteException re) { + Log.w(TAG, "Could not mark guest for deletion", re); + return false; + } + } + + /** * Sets the user as enabled, if such an user exists. * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. * Note that the default is true, it's only that managed profiles might not be enabled. diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index d1fadd6..cf407f4 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -1518,10 +1518,14 @@ public interface IMountService extends IInterface { static final int ENCRYPTION_STATE_NONE = 1; /** The volume has been encrypted succesfully. */ static final int ENCRYPTION_STATE_OK = 0; - /** The volume is in a bad state. */ + /** The volume is in a bad state.*/ static final int ENCRYPTION_STATE_ERROR_UNKNOWN = -1; - /** The volume is in a bad state - partially encrypted. Data is likely irrecoverable. */ + /** Encryption is incomplete */ static final int ENCRYPTION_STATE_ERROR_INCOMPLETE = -2; + /** Encryption is incomplete and irrecoverable */ + static final int ENCRYPTION_STATE_ERROR_INCONSISTENT = -3; + /** Underlying data is corrupt */ + static final int ENCRYPTION_STATE_ERROR_CORRUPT = -4; /** * Determines the encryption state of the volume. diff --git a/core/java/android/preference/CheckBoxPreference.java b/core/java/android/preference/CheckBoxPreference.java index 1ce98b8..fee3f0f 100644 --- a/core/java/android/preference/CheckBoxPreference.java +++ b/core/java/android/preference/CheckBoxPreference.java @@ -66,7 +66,6 @@ public class CheckBoxPreference extends TwoStatePreference { View checkboxView = view.findViewById(com.android.internal.R.id.checkbox); if (checkboxView != null && checkboxView instanceof Checkable) { ((Checkable) checkboxView).setChecked(mChecked); - sendAccessibilityEvent(checkboxView); } syncSummaryView(view); diff --git a/core/java/android/preference/SwitchPreference.java b/core/java/android/preference/SwitchPreference.java index 46be928..53b5aad 100644 --- a/core/java/android/preference/SwitchPreference.java +++ b/core/java/android/preference/SwitchPreference.java @@ -130,8 +130,6 @@ public class SwitchPreference extends TwoStatePreference { ((Checkable) checkableView).setChecked(mChecked); - sendAccessibilityEvent(checkableView); - if (checkableView instanceof Switch) { final Switch switchView = (Switch) checkableView; switchView.setTextOn(mSwitchOn); diff --git a/core/java/android/preference/TwoStatePreference.java b/core/java/android/preference/TwoStatePreference.java index 6f8be1f..3823b27 100644 --- a/core/java/android/preference/TwoStatePreference.java +++ b/core/java/android/preference/TwoStatePreference.java @@ -24,8 +24,6 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; import android.widget.TextView; /** @@ -39,7 +37,6 @@ public abstract class TwoStatePreference extends Preference { private CharSequence mSummaryOff; boolean mChecked; private boolean mCheckedSet; - private boolean mSendClickAccessibilityEvent; private boolean mDisableDependentsState; public TwoStatePreference( @@ -63,15 +60,10 @@ public abstract class TwoStatePreference extends Preference { protected void onClick() { super.onClick(); - boolean newValue = !isChecked(); - - mSendClickAccessibilityEvent = true; - - if (!callChangeListener(newValue)) { - return; + final boolean newValue = !isChecked(); + if (callChangeListener(newValue)) { + setChecked(newValue); } - - setChecked(newValue); } /** @@ -196,21 +188,6 @@ public abstract class TwoStatePreference extends Preference { : (Boolean) defaultValue); } - void sendAccessibilityEvent(View view) { - // Since the view is still not attached we create, populate, - // and send the event directly since we do not know when it - // will be attached and posting commands is not as clean. - AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(getContext()); - if (mSendClickAccessibilityEvent && accessibilityManager.isEnabled()) { - AccessibilityEvent event = AccessibilityEvent.obtain(); - event.setEventType(AccessibilityEvent.TYPE_VIEW_CLICKED); - view.onInitializeAccessibilityEvent(event); - view.dispatchPopulateAccessibilityEvent(event); - accessibilityManager.sendAccessibilityEvent(event); - } - mSendClickAccessibilityEvent = false; - } - /** * Sync a summary view contained within view's subhierarchy with the correct summary text. * @param view View where a summary should be located diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index ae11f47..f4c2dc8 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -18,6 +18,7 @@ package android.provider; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.app.SearchManager; import android.app.WallpaperManager; import android.content.ComponentName; @@ -131,6 +132,7 @@ public final class Settings { "android.settings.AIRPLANE_MODE_SETTINGS"; /** + * @hide * Activity Action: Modify Airplane mode settings using the users voice. * <p> * In some cases, a matching Activity may not exist, so ensure you safeguard against this. @@ -152,6 +154,7 @@ public final class Settings { * Output: Nothing. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + @SystemApi public static final String ACTION_VOICE_CONTROL_AIRPLANE_MODE = "android.settings.VOICE_CONTROL_AIRPLANE_MODE"; @@ -904,6 +907,15 @@ public final class Settings { public static final String ACTION_APP_NOTIFICATION_SETTINGS = "android.settings.APP_NOTIFICATION_SETTINGS"; + /** + * Activity Action: Show notification redaction settings. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_APP_NOTIFICATION_REDACTION + = "android.settings.ACTION_APP_NOTIFICATION_REDACTION"; + /** @hide */ public static final String EXTRA_APP_UID = "app_uid"; /** @hide */ public static final String EXTRA_APP_PACKAGE = "app_package"; @@ -966,11 +978,13 @@ public final class Settings { public static final String EXTRA_INPUT_METHOD_ID = "input_method_id"; /** + * @hide * Activity Extra: Enable or disable Airplane Mode. * <p> * This can be passed as an extra field to the {@link #ACTION_VOICE_CONTROL_AIRPLANE_MODE} * intent as a boolean. */ + @SystemApi public static final String EXTRA_AIRPLANE_MODE_ENABLED = "airplane_mode_enabled"; private static final String JID_RESOURCE_PREFIX = "android"; @@ -2552,7 +2566,6 @@ public final class Settings { * Call Preference String. * "SIP_ALWAYS" : Always use SIP with network access * "SIP_ADDRESS_ONLY" : Only if destination is a SIP address - * "SIP_ASK_ME_EACH_TIME" : Always ask me each time * @hide */ public static final String SIP_CALL_OPTIONS = "sip_call_options"; @@ -2570,9 +2583,13 @@ public final class Settings { public static final String SIP_ADDRESS_ONLY = "SIP_ADDRESS_ONLY"; /** - * One of the sip call options: Always ask me each time. + * @deprecated Use SIP_ALWAYS or SIP_ADDRESS_ONLY instead. Formerly used to indicate that + * the user should be prompted each time a call is made whether it should be placed using + * SIP. The {@link com.android.providers.settings.DatabaseHelper} replaces this with + * SIP_ADDRESS_ONLY. * @hide */ + @Deprecated public static final String SIP_ASK_ME_EACH_TIME = "SIP_ASK_ME_EACH_TIME"; /** @@ -3714,6 +3731,14 @@ public final class Settings { "lock_screen_allow_private_notifications"; /** + * Set by the system to track if the user needs to see the call to action for + * the lockscreen notification policy. + * @hide + */ + public static final String SHOW_NOTE_ABOUT_NOTIFICATION_HIDING = + "show_note_about_notification_hiding"; + + /** * The Logging ID (a unique 64-bit value) as a hex string. * Used as a pseudonymous identifier for logging. * @deprecated This identifier is poorly initialized and has diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java index f8bf45b..7e9aba0 100644 --- a/core/java/android/security/IKeystoreService.java +++ b/core/java/android/security/IKeystoreService.java @@ -478,6 +478,59 @@ public interface IKeystoreService extends IInterface { } return _result; } + + public int reset_uid(int uid) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + int _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeInt(uid); + mRemote.transact(Stub.TRANSACTION_reset_uid, _data, _reply, 0); + _reply.readException(); + _result = _reply.readInt(); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } + + public int sync_uid(int srcUid, int dstUid) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + int _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeInt(srcUid); + _data.writeInt(dstUid); + mRemote.transact(Stub.TRANSACTION_sync_uid, _data, _reply, 0); + _reply.readException(); + _result = _reply.readInt(); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } + + public int password_uid(String password, int uid) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + int _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeString(password); + _data.writeInt(uid); + mRemote.transact(Stub.TRANSACTION_password_uid, _data, _reply, 0); + _reply.readException(); + _result = _reply.readInt(); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } } private static final String DESCRIPTOR = "android.security.keystore"; @@ -505,6 +558,9 @@ public interface IKeystoreService extends IInterface { static final int TRANSACTION_duplicate = IBinder.FIRST_CALL_TRANSACTION + 20; static final int TRANSACTION_is_hardware_backed = IBinder.FIRST_CALL_TRANSACTION + 21; static final int TRANSACTION_clear_uid = IBinder.FIRST_CALL_TRANSACTION + 22; + static final int TRANSACTION_reset_uid = IBinder.FIRST_CALL_TRANSACTION + 23; + static final int TRANSACTION_sync_uid = IBinder.FIRST_CALL_TRANSACTION + 24; + static final int TRANSACTION_password_uid = IBinder.FIRST_CALL_TRANSACTION + 25; /** * Cast an IBinder object into an IKeystoreService interface, generating @@ -597,4 +653,10 @@ public interface IKeystoreService extends IInterface { public int is_hardware_backed(String string) throws RemoteException; public int clear_uid(long uid) throws RemoteException; + + public int reset_uid(int uid) throws RemoteException; + + public int sync_uid(int sourceUid, int targetUid) throws RemoteException; + + public int password_uid(String password, int uid) throws RemoteException; } diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index a544b2d..fc12101 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -81,6 +81,34 @@ public abstract class NotificationListenerService extends Service { * This does not change the interruption filter, only the effects. **/ public static final int HINT_HOST_DISABLE_EFFECTS = 1; + /** + * The full trim of the StatusBarNotification including all its features. + * + * @hide + */ + @SystemApi + public static final int TRIM_FULL = 0; + + /** + * A light trim of the StatusBarNotification excluding the following features: + * + * <ol> + * <li>{@link Notification#tickerView tickerView}</li> + * <li>{@link Notification#contentView contentView}</li> + * <li>{@link Notification#largeIcon largeIcon}</li> + * <li>{@link Notification#bigContentView bigContentView}</li> + * <li>{@link Notification#headsUpContentView headsUpContentView}</li> + * <li>{@link Notification#EXTRA_LARGE_ICON extras[EXTRA_LARGE_ICON]}</li> + * <li>{@link Notification#EXTRA_LARGE_ICON_BIG extras[EXTRA_LARGE_ICON_BIG]}</li> + * <li>{@link Notification#EXTRA_PICTURE extras[EXTRA_PICTURE]}</li> + * <li>{@link Notification#EXTRA_BIG_TEXT extras[EXTRA_BIG_TEXT]}</li> + * </ol> + * + * @hide + */ + @SystemApi + public static final int TRIM_LIGHT = 1; + private INotificationListenerWrapper mWrapper = null; private RankingMap mRankingMap; @@ -314,13 +342,53 @@ public abstract class NotificationListenerService extends Service { } /** + * Sets the notification trim that will be received via {@link #onNotificationPosted}. + * + * <p> + * Setting a trim other than {@link #TRIM_FULL} enables listeners that don't need access to the + * full notification features right away to reduce their memory footprint. Full notifications + * can be requested on-demand via {@link #getActiveNotifications(int)}. + * + * <p> + * Set to {@link #TRIM_FULL} initially. + * + * @hide + * + * @param trim trim of the notifications to be passed via {@link #onNotificationPosted}. + * See <code>TRIM_*</code> constants. + */ + @SystemApi + public final void setOnNotificationPostedTrim(int trim) { + if (!isBound()) return; + try { + getNotificationInterface().setOnNotificationPostedTrimFromListener(mWrapper, trim); + } catch (RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + } + } + + /** * Request the list of outstanding notifications (that is, those that are visible to the * current user). Useful when you don't know what's already been posted. * * @return An array of active notifications, sorted in natural order. */ public StatusBarNotification[] getActiveNotifications() { - return getActiveNotifications(null); + return getActiveNotifications(null, TRIM_FULL); + } + + /** + * Request the list of outstanding notifications (that is, those that are visible to the + * current user). Useful when you don't know what's already been posted. + * + * @hide + * + * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants. + * @return An array of active notifications, sorted in natural order. + */ + @SystemApi + public StatusBarNotification[] getActiveNotifications(int trim) { + return getActiveNotifications(null, trim); } /** @@ -328,14 +396,33 @@ public abstract class NotificationListenerService extends Service { * notifications but didn't want to retain the bits, and now need to go back and extract * more data out of those notifications. * + * @param keys the keys of the notifications to request * @return An array of notifications corresponding to the requested keys, in the * same order as the key list. */ public StatusBarNotification[] getActiveNotifications(String[] keys) { - if (!isBound()) return null; + return getActiveNotifications(keys, TRIM_FULL); + } + + /** + * Request one or more notifications by key. Useful if you have been keeping track of + * notifications but didn't want to retain the bits, and now need to go back and extract + * more data out of those notifications. + * + * @hide + * + * @param keys the keys of the notifications to request + * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants. + * @return An array of notifications corresponding to the requested keys, in the + * same order as the key list. + */ + @SystemApi + public StatusBarNotification[] getActiveNotifications(String[] keys, int trim) { + if (!isBound()) + return null; try { - ParceledListSlice<StatusBarNotification> parceledList = - getNotificationInterface().getActiveNotificationsFromListener(mWrapper, keys); + ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface() + .getActiveNotificationsFromListener(mWrapper, keys, trim); List<StatusBarNotification> list = parceledList.getList(); int N = list.size(); diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index 65e6988..0cde4f2 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -40,16 +40,15 @@ import java.util.Locale; /** * Top-level service of the current global voice interactor, which is providing - * support for hotwording, the back-end of a {@link android.app.VoiceInteractor}, etc. + * support for hotwording etc. * The current VoiceInteractionService that has been selected by the user is kept * always running by the system, to allow it to do things like listen for hotwords - * in the background to instigate voice interactions. + * in the background. * * <p>Because this service is always running, it should be kept as lightweight as * possible. Heavy-weight operations (including showing UI) should be implemented - * in the associated {@link android.service.voice.VoiceInteractionSessionService} when - * an actual voice interaction is taking place, and that service should run in a - * separate process from this one. + * in the associated {@link android.service.voice.VoiceInteractionSessionService} + * that only runs while the operation is active. */ public class VoiceInteractionService extends Service { /** diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 19d14bf..749f813 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -16,6 +16,7 @@ package android.service.voice; +import android.annotation.SystemApi; import android.app.Dialog; import android.app.Instrumentation; import android.content.Context; @@ -53,15 +54,7 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; /** - * An active voice interaction session, providing a facility for the implementation - * to interact with the user in the voice interaction layer. This interface is no shown - * by default, but you can request that it be shown with {@link #showWindow()}, which - * will result in a later call to {@link #onCreateContentView()} in which the UI can be - * built - * - * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish} - * when done. It can also initiate voice interactions with applications by calling - * {@link #startVoiceActivity}</p>. + * An active interaction session, started by a {@link VoiceInteractionService}. */ public abstract class VoiceInteractionSession implements KeyEvent.Callback { static final String TAG = "VoiceInteractionSession"; @@ -175,6 +168,10 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } }; + /** + * @hide + */ + @SystemApi public static class Request { final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() { @Override @@ -258,6 +255,10 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } } + /** + * @hide + */ + @SystemApi public static class Caller { final String packageName; final int uid; @@ -353,8 +354,10 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { final MyCallbacks mCallbacks = new MyCallbacks(); /** + * @hide * Information about where interesting parts of the input method UI appear. */ + @SystemApi public static final class Insets { /** * This is the part of the UI that is the main content. It is @@ -474,6 +477,10 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { mContentFrame = (FrameLayout)mRootView.findViewById(android.R.id.content); } + /** + * @hide + */ + @SystemApi public void showWindow() { if (DEBUG) Log.v(TAG, "Showing window: mWindowAdded=" + mWindowAdded + " mWindowVisible=" + mWindowVisible); @@ -502,6 +509,10 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } } + /** + * @hide + */ + @SystemApi public void hideWindow() { if (mWindowVisible) { mWindow.hide(); @@ -510,11 +521,13 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } /** + * @hide * You can call this to customize the theme used by your IME's window. * This must be set before {@link #onCreate}, so you * will typically call it in your constructor with the resource ID * of your custom theme. */ + @SystemApi public void setTheme(int theme) { if (mWindow != null) { throw new IllegalStateException("Must be called before onCreate()"); @@ -523,6 +536,7 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } /** + * @hide * Ask that a new activity be started for voice interaction. This will create a * new dedicated task in the activity manager for this voice interaction session; * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK} @@ -543,6 +557,7 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since * this is part of a voice interaction. */ + @SystemApi public void startVoiceActivity(Intent intent) { if (mToken == null) { throw new IllegalStateException("Can't call before onCreate()"); @@ -558,15 +573,19 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } /** + * @hide * Convenience for inflating views. */ + @SystemApi public LayoutInflater getLayoutInflater() { return mInflater; } /** + * @hide * Retrieve the window being used to show the session's UI. */ + @SystemApi public Dialog getWindow() { return mWindow; } @@ -612,8 +631,10 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } /** + * @hide * Hook in which to create the session's UI. */ + @SystemApi public View onCreateContentView() { return null; } @@ -626,22 +647,42 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } + /** + * @hide + */ + @SystemApi public boolean onKeyDown(int keyCode, KeyEvent event) { return false; } + /** + * @hide + */ + @SystemApi public boolean onKeyLongPress(int keyCode, KeyEvent event) { return false; } + /** + * @hide + */ + @SystemApi public boolean onKeyUp(int keyCode, KeyEvent event) { return false; } + /** + * @hide + */ + @SystemApi public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) { return false; } + /** + * @hide + */ + @SystemApi public void onBackPressed() { finish(); } @@ -656,12 +697,14 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } /** + * @hide * Compute the interesting insets into your UI. The default implementation * uses the entire window frame as the insets. The default touchable * insets are {@link Insets#TOUCHABLE_INSETS_FRAME}. * * @param outInsets Fill in with the current UI insets. */ + @SystemApi public void onComputeInsets(Insets outInsets) { int[] loc = mTmpLocation; View decor = getWindow().getWindow().getDecorView(); @@ -675,6 +718,8 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } /** + * @hide + * @SystemApi * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)} * has actually started. * @@ -686,6 +731,8 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } /** + * @hide + * @SystemApi * Called when the last activity of a task initiated by * {@link #startVoiceActivity(android.content.Intent)} has finished. The default * implementation calls {@link #finish()} on the assumption that this represents @@ -701,6 +748,8 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } /** + * @hide + * @SystemApi * Request to query for what extended commands the session supports. * * @param caller Who is making the request. @@ -715,6 +764,8 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } /** + * @hide + * @SystemApi * Request to confirm with the user before proceeding with an unrecoverable operation, * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest * VoiceInteractor.ConfirmationRequest}. @@ -730,6 +781,8 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { Bundle extras); /** + * @hide + * @SystemApi * Request to complete the voice interaction session because the voice activity successfully * completed its interaction using voice. Corresponds to * {@link android.app.VoiceInteractor.CompleteVoiceRequest @@ -751,6 +804,8 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } /** + * @hide + * @SystemApi * Request to abort the voice interaction session because the voice activity can not * complete its interaction using voice. Corresponds to * {@link android.app.VoiceInteractor.AbortVoiceRequest @@ -769,6 +824,8 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } /** + * @hide + * @SystemApi * Process an arbitrary extended command from the caller, * corresponding to a {@link android.app.VoiceInteractor.CommandRequest * VoiceInteractor.CommandRequest}. @@ -783,6 +840,8 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { public abstract void onCommand(Caller caller, Request request, String command, Bundle extras); /** + * @hide + * @SystemApi * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request} * that was previously delivered to {@link #onConfirm} or {@link #onCommand}. * diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java index ebb1a5c..eb17429 100644 --- a/core/java/android/transition/ChangeBounds.java +++ b/core/java/android/transition/ChangeBounds.java @@ -31,6 +31,7 @@ import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.util.IntProperty; import android.util.Property; import android.view.View; import android.view.ViewGroup; @@ -185,25 +186,36 @@ public class ChangeBounds extends Transition { } if (numChanges > 0) { if (!mResizeClip) { - if (startLeft != endLeft) view.setLeft(startLeft); - if (startTop != endTop) view.setTop(startTop); - if (startRight != endRight) view.setRight(startRight); - if (startBottom != endBottom) view.setBottom(startBottom); - ObjectAnimator topLeftAnimator = null; - if (startLeft != endLeft || startTop != endTop) { - Path topLeftPath = getPathMotion().getPath(startLeft, startTop, - endLeft, endTop); - topLeftAnimator = ObjectAnimator.ofInt(view, "left", "top", topLeftPath); - } - ObjectAnimator bottomRightAnimator = null; - if (startRight != endRight || startBottom != endBottom) { - Path bottomRightPath = getPathMotion().getPath(startRight, startBottom, - endRight, endBottom); - bottomRightAnimator = ObjectAnimator.ofInt(view, "right", "bottom", - bottomRightPath); + Animator anim; + if (startWidth == endWidth && startHeight == endHeight) { + view.offsetLeftAndRight(startLeft - view.getLeft()); + view.offsetTopAndBottom(startTop - view.getTop()); + Path positionPath = getPathMotion().getPath(0, 0, endLeft - startLeft, + endTop - startTop); + anim = ObjectAnimator.ofInt(view, new HorizontalOffsetProperty(), + new VerticalOffsetProperty(), positionPath); + } else { + if (startLeft != endLeft) view.setLeft(startLeft); + if (startTop != endTop) view.setTop(startTop); + if (startRight != endRight) view.setRight(startRight); + if (startBottom != endBottom) view.setBottom(startBottom); + ObjectAnimator topLeftAnimator = null; + if (startLeft != endLeft || startTop != endTop) { + Path topLeftPath = getPathMotion().getPath(startLeft, startTop, + endLeft, endTop); + topLeftAnimator = ObjectAnimator + .ofInt(view, "left", "top", topLeftPath); + } + ObjectAnimator bottomRightAnimator = null; + if (startRight != endRight || startBottom != endBottom) { + Path bottomRightPath = getPathMotion().getPath(startRight, startBottom, + endRight, endBottom); + bottomRightAnimator = ObjectAnimator.ofInt(view, "right", "bottom", + bottomRightPath); + } + anim = TransitionUtils.mergeAnimators(topLeftAnimator, + bottomRightAnimator); } - Animator anim = TransitionUtils.mergeAnimators(topLeftAnimator, - bottomRightAnimator); if (view.getParent() instanceof ViewGroup) { final ViewGroup parent = (ViewGroup) view.getParent(); parent.suppressLayout(true); @@ -341,4 +353,48 @@ public class ChangeBounds extends Transition { } return null; } + + private abstract static class OffsetProperty extends IntProperty<View> { + int mPreviousValue; + + public OffsetProperty(String name) { + super(name); + } + + @Override + public void setValue(View view, int value) { + int offset = value - mPreviousValue; + offsetBy(view, offset); + mPreviousValue = value; + } + + @Override + public Integer get(View object) { + return null; + } + + protected abstract void offsetBy(View view, int by); + } + + private static class HorizontalOffsetProperty extends OffsetProperty { + public HorizontalOffsetProperty() { + super("offsetLeftAndRight"); + } + + @Override + protected void offsetBy(View view, int by) { + view.offsetLeftAndRight(by); + } + } + + private static class VerticalOffsetProperty extends OffsetProperty { + public VerticalOffsetProperty() { + super("offsetTopAndBottom"); + } + + @Override + protected void offsetBy(View view, int by) { + view.offsetTopAndBottom(by); + } + } } diff --git a/core/java/android/transition/TransitionUtils.java b/core/java/android/transition/TransitionUtils.java index b0c9e9a..a84ecd1 100644 --- a/core/java/android/transition/TransitionUtils.java +++ b/core/java/android/transition/TransitionUtils.java @@ -40,6 +40,33 @@ public class TransitionUtils { } } + public static Transition mergeTransitions(Transition... transitions) { + int count = 0; + int nonNullIndex = -1; + for (int i = 0; i < transitions.length; i++) { + if (transitions[i] != null) { + count++; + nonNullIndex = i; + } + } + + if (count == 0) { + return null; + } + + if (count == 1) { + return transitions[nonNullIndex]; + } + + TransitionSet transitionSet = new TransitionSet(); + for (int i = 0; i < transitions.length; i++) { + if (transitions[i] != null) { + transitionSet.addTransition(transitions[i]); + } + } + return transitionSet; + } + public static class MatrixEvaluator implements TypeEvaluator<Matrix> { float[] mTempStartValues = new float[9]; diff --git a/core/java/android/util/Spline.java b/core/java/android/util/Spline.java index ed027eb..41a2e5d 100644 --- a/core/java/android/util/Spline.java +++ b/core/java/android/util/Spline.java @@ -20,15 +20,34 @@ package android.util; * Performs spline interpolation given a set of control points. * @hide */ -public final class Spline { - private final float[] mX; - private final float[] mY; - private final float[] mM; - - private Spline(float[] x, float[] y, float[] m) { - mX = x; - mY = y; - mM = m; +public abstract class Spline { + + /** + * Interpolates the value of Y = f(X) for given X. + * Clamps X to the domain of the spline. + * + * @param x The X value. + * @return The interpolated Y = f(X) value. + */ + public abstract float interpolate(float x); + + /** + * Creates an appropriate spline based on the properties of the control points. + * + * If the control points are monotonic then the resulting spline will preserve that and + * otherwise optimize for error bounds. + */ + public static Spline createSpline(float[] x, float[] y) { + if (!isStrictlyIncreasing(x)) { + throw new IllegalArgumentException("The control points must all have strictly " + + "increasing X values."); + } + + if (isMonotonic(y)) { + return createMonotoneCubicSpline(x, y); + } else { + return createLinearSpline(x, y); + } } /** @@ -50,107 +69,229 @@ public final class Spline { * @throws IllegalArgumentException if the control points are not monotonic. */ public static Spline createMonotoneCubicSpline(float[] x, float[] y) { - if (x == null || y == null || x.length != y.length || x.length < 2) { - throw new IllegalArgumentException("There must be at least two control " - + "points and the arrays must be of equal length."); - } + return new MonotoneCubicSpline(x, y); + } - final int n = x.length; - float[] d = new float[n - 1]; // could optimize this out - float[] m = new float[n]; + /** + * Creates a linear spline from a given set of control points. + * + * Like a monotone cubic spline, the interpolated curve will be monotonic if the control points + * are monotonic. + * + * @param x The X component of the control points, strictly increasing. + * @param y The Y component of the control points. + * @return + * + * @throws IllegalArgumentException if the X or Y arrays are null, have + * different lengths or have fewer than 2 values. + * @throws IllegalArgumentException if the X components of the control points are not strictly + * increasing. + */ + public static Spline createLinearSpline(float[] x, float[] y) { + return new LinearSpline(x, y); + } - // Compute slopes of secant lines between successive points. - for (int i = 0; i < n - 1; i++) { - float h = x[i + 1] - x[i]; - if (h <= 0f) { - throw new IllegalArgumentException("The control points must all " - + "have strictly increasing X values."); + private static boolean isStrictlyIncreasing(float[] x) { + if (x == null || x.length < 2) { + throw new IllegalArgumentException("There must be at least two control points."); + } + float prev = x[0]; + for (int i = 1; i < x.length; i++) { + float curr = x[i]; + if (curr <= prev) { + return false; } - d[i] = (y[i + 1] - y[i]) / h; + prev = curr; } + return true; + } - // Initialize the tangents as the average of the secants. - m[0] = d[0]; - for (int i = 1; i < n - 1; i++) { - m[i] = (d[i - 1] + d[i]) * 0.5f; + private static boolean isMonotonic(float[] x) { + if (x == null || x.length < 2) { + throw new IllegalArgumentException("There must be at least two control points."); } - m[n - 1] = d[n - 2]; - - // Update the tangents to preserve monotonicity. - for (int i = 0; i < n - 1; i++) { - if (d[i] == 0f) { // successive Y values are equal - m[i] = 0f; - m[i + 1] = 0f; - } else { - float a = m[i] / d[i]; - float b = m[i + 1] / d[i]; - if (a < 0f || b < 0f) { - throw new IllegalArgumentException("The control points must have " - + "monotonic Y values."); - } - float h = FloatMath.hypot(a, b); - if (h > 9f) { - float t = 3f / h; - m[i] = t * a * d[i]; - m[i + 1] = t * b * d[i]; - } + float prev = x[0]; + for (int i = 1; i < x.length; i++) { + float curr = x[i]; + if (curr < prev) { + return false; } + prev = curr; } - return new Spline(x, y, m); + return true; } - /** - * Interpolates the value of Y = f(X) for given X. - * Clamps X to the domain of the spline. - * - * @param x The X value. - * @return The interpolated Y = f(X) value. - */ - public float interpolate(float x) { - // Handle the boundary cases. - final int n = mX.length; - if (Float.isNaN(x)) { - return x; + public static class MonotoneCubicSpline extends Spline { + private float[] mX; + private float[] mY; + private float[] mM; + + public MonotoneCubicSpline(float[] x, float[] y) { + if (x == null || y == null || x.length != y.length || x.length < 2) { + throw new IllegalArgumentException("There must be at least two control " + + "points and the arrays must be of equal length."); + } + + final int n = x.length; + float[] d = new float[n - 1]; // could optimize this out + float[] m = new float[n]; + + // Compute slopes of secant lines between successive points. + for (int i = 0; i < n - 1; i++) { + float h = x[i + 1] - x[i]; + if (h <= 0f) { + throw new IllegalArgumentException("The control points must all " + + "have strictly increasing X values."); + } + d[i] = (y[i + 1] - y[i]) / h; + } + + // Initialize the tangents as the average of the secants. + m[0] = d[0]; + for (int i = 1; i < n - 1; i++) { + m[i] = (d[i - 1] + d[i]) * 0.5f; + } + m[n - 1] = d[n - 2]; + + // Update the tangents to preserve monotonicity. + for (int i = 0; i < n - 1; i++) { + if (d[i] == 0f) { // successive Y values are equal + m[i] = 0f; + m[i + 1] = 0f; + } else { + float a = m[i] / d[i]; + float b = m[i + 1] / d[i]; + if (a < 0f || b < 0f) { + throw new IllegalArgumentException("The control points must have " + + "monotonic Y values."); + } + float h = FloatMath.hypot(a, b); + if (h > 9f) { + float t = 3f / h; + m[i] = t * a * d[i]; + m[i + 1] = t * b * d[i]; + } + } + } + + mX = x; + mY = y; + mM = m; } - if (x <= mX[0]) { - return mY[0]; + + @Override + public float interpolate(float x) { + // Handle the boundary cases. + final int n = mX.length; + if (Float.isNaN(x)) { + return x; + } + if (x <= mX[0]) { + return mY[0]; + } + if (x >= mX[n - 1]) { + return mY[n - 1]; + } + + // Find the index 'i' of the last point with smaller X. + // We know this will be within the spline due to the boundary tests. + int i = 0; + while (x >= mX[i + 1]) { + i += 1; + if (x == mX[i]) { + return mY[i]; + } + } + + // Perform cubic Hermite spline interpolation. + float h = mX[i + 1] - mX[i]; + float t = (x - mX[i]) / h; + return (mY[i] * (1 + 2 * t) + h * mM[i] * t) * (1 - t) * (1 - t) + + (mY[i + 1] * (3 - 2 * t) + h * mM[i + 1] * (t - 1)) * t * t; } - if (x >= mX[n - 1]) { - return mY[n - 1]; + + // For debugging. + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + final int n = mX.length; + str.append("MonotoneCubicSpline{["); + for (int i = 0; i < n; i++) { + if (i != 0) { + str.append(", "); + } + str.append("(").append(mX[i]); + str.append(", ").append(mY[i]); + str.append(": ").append(mM[i]).append(")"); + } + str.append("]}"); + return str.toString(); } + } - // Find the index 'i' of the last point with smaller X. - // We know this will be within the spline due to the boundary tests. - int i = 0; - while (x >= mX[i + 1]) { - i += 1; - if (x == mX[i]) { - return mY[i]; + public static class LinearSpline extends Spline { + private final float[] mX; + private final float[] mY; + private final float[] mM; + + public LinearSpline(float[] x, float[] y) { + if (x == null || y == null || x.length != y.length || x.length < 2) { + throw new IllegalArgumentException("There must be at least two control " + + "points and the arrays must be of equal length."); + } + final int N = x.length; + mM = new float[N-1]; + for (int i = 0; i < N-1; i++) { + mM[i] = (y[i+1] - y[i]) / (x[i+1] - x[i]); } + mX = x; + mY = y; } - // Perform cubic Hermite spline interpolation. - float h = mX[i + 1] - mX[i]; - float t = (x - mX[i]) / h; - return (mY[i] * (1 + 2 * t) + h * mM[i] * t) * (1 - t) * (1 - t) - + (mY[i + 1] * (3 - 2 * t) + h * mM[i + 1] * (t - 1)) * t * t; - } + @Override + public float interpolate(float x) { + // Handle the boundary cases. + final int n = mX.length; + if (Float.isNaN(x)) { + return x; + } + if (x <= mX[0]) { + return mY[0]; + } + if (x >= mX[n - 1]) { + return mY[n - 1]; + } - // For debugging. - @Override - public String toString() { - StringBuilder str = new StringBuilder(); - final int n = mX.length; - str.append("["); - for (int i = 0; i < n; i++) { - if (i != 0) { - str.append(", "); - } - str.append("(").append(mX[i]); - str.append(", ").append(mY[i]); - str.append(": ").append(mM[i]).append(")"); + // Find the index 'i' of the last point with smaller X. + // We know this will be within the spline due to the boundary tests. + int i = 0; + while (x >= mX[i + 1]) { + i += 1; + if (x == mX[i]) { + return mY[i]; + } + } + return mY[i] + mM[i] * (x - mX[i]); + } + + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + final int n = mX.length; + str.append("LinearSpline{["); + for (int i = 0; i < n; i++) { + if (i != 0) { + str.append(", "); + } + str.append("(").append(mX[i]); + str.append(", ").append(mY[i]); + if (i < n-1) { + str.append(": ").append(mM[i]); + } + str.append(")"); + } + str.append("]}"); + return str.toString(); } - str.append("]"); - return str.toString(); } } diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index 477c994..a10dda3 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -18,6 +18,7 @@ package android.view; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.Region; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -96,7 +97,7 @@ final class AccessibilityInteractionController { } public void findAccessibilityNodeInfoByAccessibilityIdClientThread( - long accessibilityNodeId, int interactionId, + long accessibilityNodeId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); @@ -109,6 +110,7 @@ final class AccessibilityInteractionController { args.argi3 = interactionId; args.arg1 = callback; args.arg2 = spec; + args.arg3 = interactiveRegion; message.obj = args; // If the interrogation is performed by the same thread as the main UI @@ -133,6 +135,7 @@ final class AccessibilityInteractionController { final IAccessibilityInteractionConnectionCallback callback = (IAccessibilityInteractionConnectionCallback) args.arg1; final MagnificationSpec spec = (MagnificationSpec) args.arg2; + final Region interactiveRegion = (Region) args.arg3; args.recycle(); @@ -159,6 +162,7 @@ final class AccessibilityInteractionController { if (spec != null) { spec.recycle(); } + adjustIsVisibleToUserIfNeeded(infos, interactiveRegion); callback.setFindAccessibilityNodeInfosResult(infos, interactionId); infos.clear(); } catch (RemoteException re) { @@ -168,8 +172,9 @@ final class AccessibilityInteractionController { } public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId, - String viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback, - int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { + String viewId, Region interactiveRegion, int interactionId, + IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, + long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID; message.arg1 = flags; @@ -180,6 +185,7 @@ final class AccessibilityInteractionController { args.arg1 = callback; args.arg2 = spec; args.arg3 = viewId; + args.arg4 = interactiveRegion; message.obj = args; @@ -205,6 +211,7 @@ final class AccessibilityInteractionController { (IAccessibilityInteractionConnectionCallback) args.arg1; final MagnificationSpec spec = (MagnificationSpec) args.arg2; final String viewId = (String) args.arg3; + final Region interactiveRegion = (Region) args.arg4; args.recycle(); @@ -241,6 +248,7 @@ final class AccessibilityInteractionController { if (spec != null) { spec.recycle(); } + adjustIsVisibleToUserIfNeeded(infos, interactiveRegion); callback.setFindAccessibilityNodeInfosResult(infos, interactionId); } catch (RemoteException re) { /* ignore - the other side will time out */ @@ -249,8 +257,9 @@ final class AccessibilityInteractionController { } public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId, - String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, - int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { + String text, Region interactiveRegion, int interactionId, + IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, + long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT; message.arg1 = flags; @@ -262,6 +271,7 @@ final class AccessibilityInteractionController { args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId); args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId); args.argi3 = interactionId; + args.arg4 = interactiveRegion; message.obj = args; // If the interrogation is performed by the same thread as the main UI @@ -287,6 +297,7 @@ final class AccessibilityInteractionController { final int accessibilityViewId = args.argi1; final int virtualDescendantId = args.argi2; final int interactionId = args.argi3; + final Region interactiveRegion = (Region) args.arg4; args.recycle(); List<AccessibilityNodeInfo> infos = null; @@ -347,6 +358,7 @@ final class AccessibilityInteractionController { if (spec != null) { spec.recycle(); } + adjustIsVisibleToUserIfNeeded(infos, interactiveRegion); callback.setFindAccessibilityNodeInfosResult(infos, interactionId); } catch (RemoteException re) { /* ignore - the other side will time out */ @@ -354,7 +366,8 @@ final class AccessibilityInteractionController { } } - public void findFocusClientThread(long accessibilityNodeId, int focusType, int interactionId, + public void findFocusClientThread(long accessibilityNodeId, int focusType, + Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid, long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); @@ -368,6 +381,7 @@ final class AccessibilityInteractionController { args.argi3 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId); args.arg1 = callback; args.arg2 = spec; + args.arg3 = interactiveRegion; message.obj = args; @@ -394,6 +408,7 @@ final class AccessibilityInteractionController { final IAccessibilityInteractionConnectionCallback callback = (IAccessibilityInteractionConnectionCallback) args.arg1; final MagnificationSpec spec = (MagnificationSpec) args.arg2; + final Region interactiveRegion = (Region) args.arg3; args.recycle(); AccessibilityNodeInfo focused = null; @@ -457,6 +472,7 @@ final class AccessibilityInteractionController { if (spec != null) { spec.recycle(); } + adjustIsVisibleToUserIfNeeded(focused, interactiveRegion); callback.setFindAccessibilityNodeInfoResult(focused, interactionId); } catch (RemoteException re) { /* ignore - the other side will time out */ @@ -464,7 +480,8 @@ final class AccessibilityInteractionController { } } - public void focusSearchClientThread(long accessibilityNodeId, int direction, int interactionId, + public void focusSearchClientThread(long accessibilityNodeId, int direction, + Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid, long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); @@ -477,6 +494,7 @@ final class AccessibilityInteractionController { args.argi3 = interactionId; args.arg1 = callback; args.arg2 = spec; + args.arg3 = interactiveRegion; message.obj = args; @@ -502,6 +520,7 @@ final class AccessibilityInteractionController { final IAccessibilityInteractionConnectionCallback callback = (IAccessibilityInteractionConnectionCallback) args.arg1; final MagnificationSpec spec = (MagnificationSpec) args.arg2; + final Region interactiveRegion = (Region) args.arg3; args.recycle(); @@ -530,6 +549,7 @@ final class AccessibilityInteractionController { if (spec != null) { spec.recycle(); } + adjustIsVisibleToUserIfNeeded(next, interactiveRegion); callback.setFindAccessibilityNodeInfoResult(next, interactionId); } catch (RemoteException re) { /* ignore - the other side will time out */ @@ -644,6 +664,30 @@ final class AccessibilityInteractionController { } } + private void adjustIsVisibleToUserIfNeeded(List<AccessibilityNodeInfo> infos, + Region interactiveRegion) { + if (interactiveRegion == null || infos == null) { + return; + } + final int infoCount = infos.size(); + for (int i = 0; i < infoCount; i++) { + AccessibilityNodeInfo info = infos.get(i); + adjustIsVisibleToUserIfNeeded(info, interactiveRegion); + } + } + + private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info, + Region interactiveRegion) { + if (interactiveRegion == null || info == null) { + return; + } + Rect boundsInScreen = mTempRect; + info.getBoundsInScreen(boundsInScreen); + if (interactiveRegion.quickReject(boundsInScreen)) { + info.setVisibleToUser(false); + } + } + private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info, MagnificationSpec spec) { if (info == null) { diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java index 063a08d..7dcad68 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -149,9 +149,9 @@ public final class PointerIcon implements Parcelable { * Creates a custom pointer from the given bitmap and hotspot information. * * @param bitmap The bitmap for the icon. - * @param hotspotX The X offset of the pointer icon hotspot in the bitmap. + * @param hotSpotX The X offset of the pointer icon hotspot in the bitmap. * Must be within the [0, bitmap.getWidth()) range. - * @param hotspotY The Y offset of the pointer icon hotspot in the bitmap. + * @param hotSpotY The Y offset of the pointer icon hotspot in the bitmap. * Must be within the [0, bitmap.getHeight()) range. * @return A pointer icon for this bitmap. * @@ -374,18 +374,18 @@ public final class PointerIcon implements Parcelable { } private void loadResource(Context context, Resources resources, int resourceId) { - XmlResourceParser parser = resources.getXml(resourceId); + final XmlResourceParser parser = resources.getXml(resourceId); final int bitmapRes; final float hotSpotX; final float hotSpotY; try { XmlUtils.beginDocument(parser, "pointer-icon"); - TypedArray a = resources.obtainAttributes( + final TypedArray a = resources.obtainAttributes( parser, com.android.internal.R.styleable.PointerIcon); bitmapRes = a.getResourceId(com.android.internal.R.styleable.PointerIcon_bitmap, 0); - hotSpotX = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0); - hotSpotY = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0); + hotSpotX = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0); + hotSpotY = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0); a.recycle(); } catch (Exception ex) { throw new IllegalArgumentException("Exception parsing pointer icon resource.", ex); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 49d925f..dd1cbc9 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -6629,14 +6629,15 @@ public final class ViewRootImpl implements ViewParent, @Override public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, - int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, + Region interactiveRegion, int interactionId, + IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId, - interactionId, callback, flags, interrogatingPid, interrogatingTid, - spec); + interactiveRegion, interactionId, callback, flags, interrogatingPid, + interrogatingTid, spec); } else { // We cannot make the call and notify the caller so it does not wait. try { @@ -6669,15 +6670,15 @@ public final class ViewRootImpl implements ViewParent, @Override public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, - String viewId, int interactionId, + String viewId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .findAccessibilityNodeInfosByViewIdClientThread(accessibilityNodeId, - viewId, interactionId, callback, flags, interrogatingPid, - interrogatingTid, spec); + viewId, interactiveRegion, interactionId, callback, flags, + interrogatingPid, interrogatingTid, spec); } else { // We cannot make the call and notify the caller so it does not wait. try { @@ -6690,14 +6691,15 @@ public final class ViewRootImpl implements ViewParent, @Override public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, - int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, + Region interactiveRegion, int interactionId, + IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text, - interactionId, callback, flags, interrogatingPid, interrogatingTid, - spec); + interactiveRegion, interactionId, callback, flags, interrogatingPid, + interrogatingTid, spec); } else { // We cannot make the call and notify the caller so it does not wait. try { @@ -6709,14 +6711,15 @@ public final class ViewRootImpl implements ViewParent, } @Override - public void findFocus(long accessibilityNodeId, int focusType, int interactionId, - IAccessibilityInteractionConnectionCallback callback, int flags, + public void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion, + int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() - .findFocusClientThread(accessibilityNodeId, focusType, interactionId, callback, - flags, interrogatingPid, interrogatingTid, spec); + .findFocusClientThread(accessibilityNodeId, focusType, interactiveRegion, + interactionId, callback, flags, interrogatingPid, interrogatingTid, + spec); } else { // We cannot make the call and notify the caller so it does not wait. try { @@ -6728,14 +6731,15 @@ public final class ViewRootImpl implements ViewParent, } @Override - public void focusSearch(long accessibilityNodeId, int direction, int interactionId, - IAccessibilityInteractionConnectionCallback callback, int flags, + public void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion, + int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() - .focusSearchClientThread(accessibilityNodeId, direction, interactionId, - callback, flags, interrogatingPid, interrogatingTid, spec); + .focusSearchClientThread(accessibilityNodeId, direction, interactiveRegion, + interactionId, callback, flags, interrogatingPid, interrogatingTid, + spec); } else { // We cannot make the call and notify the caller so it does not wait. try { diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 9b6f200..ebc683a 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -1430,7 +1430,9 @@ public abstract class Window { * {@link android.transition.Visibility} as entering is governed by changing visibility from * {@link View#INVISIBLE} to {@link View#VISIBLE}. If <code>transition</code> is null, * entering Views will remain unaffected. + * * @param transition The Transition to use to move Views into the initial Scene. + * @attr ref android.R.styleable#Window_windowEnterTransition */ public void setEnterTransition(Transition transition) {} @@ -1444,8 +1446,10 @@ public abstract class Window { * {@link View#VISIBLE} to {@link View#INVISIBLE}. If <code>transition</code> is null, * entering Views will remain unaffected. If nothing is set, the default will be to * use the same value as set in {@link #setEnterTransition(android.transition.Transition)}. + * * @param transition The Transition to use to move Views out of the Scene when the Window * is preparing to close. + * @attr ref android.R.styleable#Window_windowReturnTransition */ public void setReturnTransition(Transition transition) {} @@ -1456,8 +1460,10 @@ public abstract class Window { * {@link android.transition.Visibility} as exiting is governed by changing visibility * from {@link View#VISIBLE} to {@link View#INVISIBLE}. If transition is null, the views will * remain unaffected. Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * * @param transition The Transition to use to move Views out of the scene when calling a * new Activity. + * @attr ref android.R.styleable#Window_windowExitTransition */ public void setExitTransition(Transition transition) {} @@ -1470,8 +1476,10 @@ public abstract class Window { * the views will remain unaffected. If nothing is set, the default will be to use the same * transition as {@link #setExitTransition(android.transition.Transition)}. * Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * * @param transition The Transition to use to move Views into the scene when reentering from a * previously-started Activity. + * @attr ref android.R.styleable#Window_windowReenterTransition */ public void setReenterTransition(Transition transition) {} @@ -1484,6 +1492,7 @@ public abstract class Window { * entering Views will remain unaffected. Requires {@link #FEATURE_CONTENT_TRANSITIONS}. * * @return the Transition to use to move Views into the initial Scene. + * @attr ref android.R.styleable#Window_windowEnterTransition */ public Transition getEnterTransition() { return null; } @@ -1495,8 +1504,10 @@ public abstract class Window { * {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend * {@link android.transition.Visibility} as entering is governed by changing visibility from * {@link View#VISIBLE} to {@link View#INVISIBLE}. + * * @return The Transition to use to move Views out of the Scene when the Window * is preparing to close. + * @attr ref android.R.styleable#Window_windowReturnTransition */ public Transition getReturnTransition() { return null; } @@ -1507,8 +1518,10 @@ public abstract class Window { * {@link android.transition.Visibility} as exiting is governed by changing visibility * from {@link View#VISIBLE} to {@link View#INVISIBLE}. If transition is null, the views will * remain unaffected. Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * * @return the Transition to use to move Views out of the scene when calling a * new Activity. + * @attr ref android.R.styleable#Window_windowExitTransition */ public Transition getExitTransition() { return null; } @@ -1519,8 +1532,10 @@ public abstract class Window { * will extend {@link android.transition.Visibility} as exiting is governed by changing * visibility from {@link View#VISIBLE} to {@link View#INVISIBLE}. * Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * * @return The Transition to use to move Views into the scene when reentering from a * previously-started Activity. + * @attr ref android.R.styleable#Window_windowReenterTransition */ public Transition getReenterTransition() { return null; } @@ -1530,8 +1545,10 @@ public abstract class Window { * {@link android.transition.ChangeBounds}. A null * value will cause transferred shared elements to blink to the final position. * Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * * @param transition The Transition to use for shared elements transferred into the content * Scene. + * @attr ref android.R.styleable#Window_windowSharedElementEnterTransition */ public void setSharedElementEnterTransition(Transition transition) {} @@ -1543,22 +1560,28 @@ public abstract class Window { * If no value is set, the default will be to use the same value as * {@link #setSharedElementEnterTransition(android.transition.Transition)}. * Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * * @param transition The Transition to use for shared elements transferred out of the content * Scene. + * @attr ref android.R.styleable#Window_windowSharedElementReturnTransition */ public void setSharedElementReturnTransition(Transition transition) {} /** * Returns the Transition that will be used for shared elements transferred into the content * Scene. Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * * @return Transition to use for sharend elements transferred into the content Scene. + * @attr ref android.R.styleable#Window_windowSharedElementEnterTransition */ public Transition getSharedElementEnterTransition() { return null; } /** * Returns the Transition that will be used for shared elements transferred back to a * calling Activity. Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * * @return Transition to use for sharend elements transferred into the content Scene. + * @attr ref android.R.styleable#Window_windowSharedElementReturnTransition */ public Transition getSharedElementReturnTransition() { return null; } @@ -1568,8 +1591,10 @@ public abstract class Window { * must animate during the exit transition, this Transition should be used. Upon completion, * the shared elements may be transferred to the started Activity. * Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * * @param transition The Transition to use for shared elements in the launching Window * prior to transferring to the launched Activity's Window. + * @attr ref android.R.styleable#Window_windowSharedElementExitTransition */ public void setSharedElementExitTransition(Transition transition) {} @@ -1579,8 +1604,10 @@ public abstract class Window { * is set, this will default to * {@link #setSharedElementExitTransition(android.transition.Transition)}. * Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * * @param transition The Transition to use for shared elements in the launching Window * after the shared element has returned to the Window. + * @attr ref android.R.styleable#Window_windowSharedElementReenterTransition */ public void setSharedElementReenterTransition(Transition transition) {} @@ -1591,6 +1618,7 @@ public abstract class Window { * * @return the Transition to use for shared elements in the launching Window prior * to transferring to the launched Activity's Window. + * @attr ref android.R.styleable#Window_windowSharedElementExitTransition */ public Transition getSharedElementExitTransition() { return null; } @@ -1601,6 +1629,7 @@ public abstract class Window { * * @return the Transition that will be used for shared elements reentering from a started * Activity after it has returned the shared element to it start location. + * @attr ref android.R.styleable#Window_windowSharedElementReenterTransition */ public Transition getSharedElementReenterTransition() { return null; } @@ -1610,8 +1639,10 @@ public abstract class Window { * transition of the calling Activity. When true, the transition will start as soon as possible. * When false, the transition will wait until the remote exiting transition completes before * starting. + * * @param allow true to start the enter transition when possible or false to * wait until the exiting transition completes. + * @attr ref android.R.styleable#Window_windowAllowEnterTransitionOverlap */ public void setAllowEnterTransitionOverlap(boolean allow) {} @@ -1621,8 +1652,10 @@ public abstract class Window { * transition of the calling Activity. When true, the transition will start as soon as possible. * When false, the transition will wait until the remote exiting transition completes before * starting. + * * @return true when the enter transition should start as soon as possible or false to * when it should wait until the exiting transition completes. + * @attr ref android.R.styleable#Window_windowAllowEnterTransitionOverlap */ public boolean getAllowEnterTransitionOverlap() { return true; } @@ -1632,10 +1665,20 @@ public abstract class Window { * transition of the called Activity when reentering after if finishes. When true, * the transition will start as soon as possible. When false, the transition will wait * until the called Activity's exiting transition completes before starting. + * * @param allow true to start the transition when possible or false to wait until the * called Activity's exiting transition completes. + * @attr ref android.R.styleable#Window_windowAllowReturnTransitionOverlap */ - public void setAllowExitTransitionOverlap(boolean allow) {} + public void setAllowReturnTransitionOverlap(boolean allow) {} + + /** + * TODO: remove this. + * @hide + */ + public void setAllowExitTransitionOverlap(boolean allow) { + setAllowReturnTransitionOverlap(allow); + } /** * Returns how the transition set in @@ -1643,10 +1686,18 @@ public abstract class Window { * transition of the called Activity when reentering after if finishes. When true, * the transition will start as soon as possible. When false, the transition will wait * until the called Activity's exiting transition completes before starting. + * * @return true when the transition should start when possible or false when it should wait * until the called Activity's exiting transition completes. + * @attr ref android.R.styleable#Window_windowAllowReturnTransitionOverlap */ - public boolean getAllowExitTransitionOverlap() { return true; } + public boolean getAllowReturnTransitionOverlap() { return true; } + + /** + * TODO: remove this. + * @hide + */ + public boolean getAllowExitTransitionOverlap() { return getAllowReturnTransitionOverlap(); } /** * Returns the duration, in milliseconds, of the window background fade @@ -1654,8 +1705,10 @@ public abstract class Window { * <p>When executing the enter transition, the background starts transparent * and fades in. This requires {@link #FEATURE_CONTENT_TRANSITIONS}. The default is * 300 milliseconds.</p> + * * @return The duration of the window background fade to opaque during enter transition. * @see #getEnterTransition() + * @attr ref android.R.styleable#Window_windowTransitionBackgroundFadeDuration */ public long getTransitionBackgroundFadeDuration() { return 0; } @@ -1665,9 +1718,11 @@ public abstract class Window { * <p>When executing the enter transition, the background starts transparent * and fades in. This requires {@link #FEATURE_CONTENT_TRANSITIONS}. The default is * 300 milliseconds.</p> + * * @param fadeDurationMillis The duration of the window background fade to or from opaque * during enter transition. * @see #setEnterTransition(android.transition.Transition) + * @attr ref android.R.styleable#Window_windowTransitionBackgroundFadeDuration */ public void setTransitionBackgroundFadeDuration(long fadeDurationMillis) { } @@ -1679,6 +1734,7 @@ public abstract class Window { * @return <code>true</code> when shared elements should use an Overlay during * shared element transitions or <code>false</code> when they should animate as * part of the normal View hierarchy. + * @attr ref android.R.styleable#Window_windowSharedElementsUseOverlay */ public boolean getSharedElementsUseOverlay() { return true; } @@ -1689,6 +1745,7 @@ public abstract class Window { * @param sharedElementsUseOverlay <code>true</code> indicates that shared elements should * be transitioned with an Overlay or <code>false</code> * to transition within the normal View hierarchy. + * @attr ref android.R.styleable#Window_windowSharedElementsUseOverlay */ public void setSharedElementsUseOverlay(boolean sharedElementsUseOverlay) { } diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl index 8d15472..faf7789 100644 --- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl @@ -16,6 +16,7 @@ package android.view.accessibility; +import android.graphics.Region; import android.os.Bundle; import android.view.MagnificationSpec; import android.view.accessibility.AccessibilityNodeInfo; @@ -29,23 +30,23 @@ import android.view.accessibility.IAccessibilityInteractionConnectionCallback; */ oneway interface IAccessibilityInteractionConnection { - void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, int interactionId, - IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid, in MagnificationSpec spec); + void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, in Region bounds, + int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, + int interrogatingPid, long interrogatingTid, in MagnificationSpec spec); void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId, + in Region bounds, int interactionId, IAccessibilityInteractionConnectionCallback callback, + int flags, int interrogatingPid, long interrogatingTid, in MagnificationSpec spec); + + void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, in Region bounds, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, in MagnificationSpec spec); - void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, int interactionId, - IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, - long interrogatingTid, in MagnificationSpec spec); - - void findFocus(long accessibilityNodeId, int focusType, int interactionId, + void findFocus(long accessibilityNodeId, int focusType, in Region bounds, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, in MagnificationSpec spec); - void focusSearch(long accessibilityNodeId, int direction, int interactionId, + void focusSearch(long accessibilityNodeId, int direction, in Region bounds, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, in MagnificationSpec spec); diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 4d2f57a..20adfe4 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -431,7 +431,15 @@ public class BaseInputConnection implements InputConnection { /** * The default implementation does nothing. */ - public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) { + public boolean requestCursorUpdates(int cursorUpdateMode) { + return false; + } + + /** + * The default implementation does nothing. + * @removed + */ + public final boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) { return false; } diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java index 730b7f6..fe0f5b9 100644 --- a/core/java/android/view/inputmethod/CursorAnchorInfo.java +++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java @@ -45,9 +45,9 @@ public final class CursorAnchorInfo implements Parcelable { private final CharSequence mComposingText; /** - * {@code True} if the insertion marker is partially or entirely clipped by other UI elements. + * Flags of the insertion marker. See {@link #FLAG_HAS_VISIBLE_REGION} for example. */ - private final boolean mInsertionMarkerClipped; + private final int mInsertionMarkerFlags; /** * Horizontal position of the insertion marker, in the local coordinates that will be * transformed with the transformation matrix when rendered on the screen. This should be @@ -90,27 +90,47 @@ public final class CursorAnchorInfo implements Parcelable { */ private final Matrix mMatrix; + /** + * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterRectFlags(int)}: the + * insertion marker or character bounds have at least one visible region. + */ + public static final int FLAG_HAS_VISIBLE_REGION = 0x01; + + /** + * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterRectFlags(int)}: the + * insertion marker or character bounds have at least one invisible (clipped) region. + */ + public static final int FLAG_HAS_INVISIBLE_REGION = 0x02; + + /** + * @removed + */ public static final int CHARACTER_RECT_TYPE_MASK = 0x0f; /** * Type for {@link #CHARACTER_RECT_TYPE_MASK}: the editor did not specify any type of this * character. Editor authors should not use this flag. + * @removed */ public static final int CHARACTER_RECT_TYPE_UNSPECIFIED = 0; /** * Type for {@link #CHARACTER_RECT_TYPE_MASK}: the character is entirely visible. + * @removed */ public static final int CHARACTER_RECT_TYPE_FULLY_VISIBLE = 1; /** * Type for {@link #CHARACTER_RECT_TYPE_MASK}: some area of the character is invisible. + * @removed */ public static final int CHARACTER_RECT_TYPE_PARTIALLY_VISIBLE = 2; /** * Type for {@link #CHARACTER_RECT_TYPE_MASK}: the character is entirely invisible. + * @removed */ public static final int CHARACTER_RECT_TYPE_INVISIBLE = 3; /** * Type for {@link #CHARACTER_RECT_TYPE_MASK}: the editor gave up to calculate the rectangle * for this character. Input method authors should ignore the returned rectangle. + * @removed */ public static final int CHARACTER_RECT_TYPE_NOT_FEASIBLE = 4; @@ -119,7 +139,7 @@ public final class CursorAnchorInfo implements Parcelable { mSelectionEnd = source.readInt(); mComposingTextStart = source.readInt(); mComposingText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); - mInsertionMarkerClipped = (source.readInt() != 0); + mInsertionMarkerFlags = source.readInt(); mInsertionMarkerHorizontal = source.readFloat(); mInsertionMarkerTop = source.readFloat(); mInsertionMarkerBaseline = source.readFloat(); @@ -141,7 +161,7 @@ public final class CursorAnchorInfo implements Parcelable { dest.writeInt(mSelectionEnd); dest.writeInt(mComposingTextStart); TextUtils.writeToParcel(mComposingText, dest, flags); - dest.writeInt(mInsertionMarkerClipped ? 1 : 0); + dest.writeInt(mInsertionMarkerFlags); dest.writeFloat(mInsertionMarkerHorizontal); dest.writeFloat(mInsertionMarkerTop); dest.writeFloat(mInsertionMarkerBaseline); @@ -159,7 +179,7 @@ public final class CursorAnchorInfo implements Parcelable { + mInsertionMarkerBaseline + mInsertionMarkerBottom; int hash = floatHash > 0 ? (int) floatHash : (int)(-floatHash); hash *= 31; - hash += (mInsertionMarkerClipped ? 2 : 1); + hash += mInsertionMarkerFlags; hash *= 31; hash += mSelectionStart + mSelectionEnd + mComposingTextStart; hash *= 31; @@ -204,16 +224,13 @@ public final class CursorAnchorInfo implements Parcelable { || !Objects.equals(mComposingText, that.mComposingText)) { return false; } - if (mInsertionMarkerClipped != that.mInsertionMarkerClipped + if (mInsertionMarkerFlags != that.mInsertionMarkerFlags || !areSameFloatImpl(mInsertionMarkerHorizontal, that.mInsertionMarkerHorizontal) || !areSameFloatImpl(mInsertionMarkerTop, that.mInsertionMarkerTop) || !areSameFloatImpl(mInsertionMarkerBaseline, that.mInsertionMarkerBaseline) || !areSameFloatImpl(mInsertionMarkerBottom, that.mInsertionMarkerBottom)) { return false; } - if (!Objects.equals(mComposingTextStart, that.mComposingTextStart)) { - return false; - } if (!Objects.equals(mCharacterRects, that.mCharacterRects)) { return false; } @@ -228,7 +245,7 @@ public final class CursorAnchorInfo implements Parcelable { return "SelectionInfo{mSelection=" + mSelectionStart + "," + mSelectionEnd + " mComposingTextStart=" + mComposingTextStart + " mComposingText=" + Objects.toString(mComposingText) - + " mInsertionMarkerClipped=" + mInsertionMarkerClipped + + " mInsertionMarkerFlags=" + mInsertionMarkerFlags + " mInsertionMarkerHorizontal=" + mInsertionMarkerHorizontal + " mInsertionMarkerTop=" + mInsertionMarkerTop + " mInsertionMarkerBaseline=" + mInsertionMarkerBaseline @@ -275,6 +292,20 @@ public final class CursorAnchorInfo implements Parcelable { private CharSequence mComposingText = null; /** + * @removed + */ + public Builder setInsertionMarkerLocation(final float horizontalPosition, + final float lineTop, final float lineBaseline, final float lineBottom, + final boolean clipped){ + mInsertionMarkerHorizontal = horizontalPosition; + mInsertionMarkerTop = lineTop; + mInsertionMarkerBaseline = lineBaseline; + mInsertionMarkerBottom = lineBottom; + mInsertionMarkerFlags = clipped ? FLAG_HAS_INVISIBLE_REGION : 0; + return this; + } + + /** * Sets the location of the text insertion point (zero width cursor) as a rectangle in * local coordinates. Calling this can be skipped when there is no text insertion point; * however if there is an insertion point, editors must call this method. @@ -291,24 +322,24 @@ public final class CursorAnchorInfo implements Parcelable { * @param lineBottom vertical position of the insertion marker, in the local coordinates * that will be transformed with the transformation matrix when rendered on the screen. This * should be calculated or compatible with {@link Layout#getLineBottom(int)}. - * @param clipped {@code true} is the insertion marker is partially or entierly clipped by - * other UI elements. + * @param flags flags of the insertion marker. See {@link #FLAG_HAS_VISIBLE_REGION} for + * example. */ public Builder setInsertionMarkerLocation(final float horizontalPosition, final float lineTop, final float lineBaseline, final float lineBottom, - final boolean clipped){ + final int flags){ mInsertionMarkerHorizontal = horizontalPosition; mInsertionMarkerTop = lineTop; mInsertionMarkerBaseline = lineBaseline; mInsertionMarkerBottom = lineBottom; - mInsertionMarkerClipped = clipped; + mInsertionMarkerFlags = flags; return this; } private float mInsertionMarkerHorizontal = Float.NaN; private float mInsertionMarkerTop = Float.NaN; private float mInsertionMarkerBaseline = Float.NaN; private float mInsertionMarkerBottom = Float.NaN; - private boolean mInsertionMarkerClipped = false; + private int mInsertionMarkerFlags = 0; /** * Adds the bounding box of the character specified with the index. @@ -323,8 +354,8 @@ public final class CursorAnchorInfo implements Parcelable { * coordinates, that is, right edge for LTR text and left edge for RTL text. * @param trailingEdgeY y coordinate of the trailing edge of the character in local * coordinates. - * @param flags type and flags for this character. See - * {@link #CHARACTER_RECT_TYPE_FULLY_VISIBLE} for example. + * @param flags flags for this character rect. See {@link #FLAG_HAS_VISIBLE_REGION} for + * example. * @throws IllegalArgumentException If the index is a negative value, or not greater than * all of the previously called indices. */ @@ -334,11 +365,6 @@ public final class CursorAnchorInfo implements Parcelable { if (index < 0) { throw new IllegalArgumentException("index must not be a negative integer."); } - final int type = flags & CHARACTER_RECT_TYPE_MASK; - if (type == CHARACTER_RECT_TYPE_UNSPECIFIED) { - throw new IllegalArgumentException("Type except for " - + "CHARACTER_RECT_TYPE_UNSPECIFIED must be specified."); - } if (mCharacterRectBuilder == null) { mCharacterRectBuilder = new SparseRectFArrayBuilder(); } @@ -391,7 +417,7 @@ public final class CursorAnchorInfo implements Parcelable { mSelectionEnd = -1; mComposingTextStart = -1; mComposingText = null; - mInsertionMarkerClipped = false; + mInsertionMarkerFlags = 0; mInsertionMarkerHorizontal = Float.NaN; mInsertionMarkerTop = Float.NaN; mInsertionMarkerBaseline = Float.NaN; @@ -409,7 +435,7 @@ public final class CursorAnchorInfo implements Parcelable { mSelectionEnd = builder.mSelectionEnd; mComposingTextStart = builder.mComposingTextStart; mComposingText = builder.mComposingText; - mInsertionMarkerClipped = builder.mInsertionMarkerClipped; + mInsertionMarkerFlags = builder.mInsertionMarkerFlags; mInsertionMarkerHorizontal = builder.mInsertionMarkerHorizontal; mInsertionMarkerTop = builder.mInsertionMarkerTop; mInsertionMarkerBaseline = builder.mInsertionMarkerBaseline; @@ -452,11 +478,20 @@ public final class CursorAnchorInfo implements Parcelable { } /** + * Returns the flag of the insertion marker. + * @return the flag of the insertion marker. {@code 0} if no flag is specified. + */ + public int getInsertionMarkerFlags() { + return mInsertionMarkerFlags; + } + + /** * Returns the visibility of the insertion marker. * @return {@code true} if the insertion marker is partially or entirely clipped. + * @removed */ public boolean isInsertionMarkerClipped() { - return mInsertionMarkerClipped; + return (mInsertionMarkerFlags & FLAG_HAS_VISIBLE_REGION) != 0; } /** @@ -525,17 +560,17 @@ public final class CursorAnchorInfo implements Parcelable { } /** - * Returns the flags associated with the character specified with the index. + * Returns the flags associated with the character rect specified with the index. * @param index index of the character in a Java chars. - * @return {@link #CHARACTER_RECT_TYPE_UNSPECIFIED} if no flag is specified. + * @return {@code 0} if no flag is specified. */ // TODO: Prepare a document about the expected behavior for surrogate pairs, combining // characters, and non-graphical chars. public int getCharacterRectFlags(final int index) { if (mCharacterRects == null) { - return CHARACTER_RECT_TYPE_UNSPECIFIED; + return 0; } - return mCharacterRects.getFlags(index, CHARACTER_RECT_TYPE_UNSPECIFIED); + return mCharacterRects.getFlags(index, 0); } /** diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index ca094c1..093fb2f 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -728,31 +728,47 @@ public interface InputConnection { * The editor is requested to call * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} at * once, as soon as possible, regardless of cursor/anchor position changes. This flag can be - * used together with {@link #REQUEST_UPDATE_CURSOR_ANCHOR_INFO_MONITOR}. + * used together with {@link #CURSOR_UPDATE_MONITOR}. */ - public static final int REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE = 1 << 0; + public static final int CURSOR_UPDATE_IMMEDIATE = 1 << 0; /** * The editor is requested to call * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} * whenever cursor/anchor position is changed. To disable monitoring, call - * {@link InputConnection#requestUpdateCursorAnchorInfo(int)} again with this flag off. + * {@link InputConnection#requestCursorUpdates(int)} again with this flag off. * <p> - * This flag can be used together with {@link #REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE}. + * This flag can be used together with {@link #CURSOR_UPDATE_IMMEDIATE}. * </p> */ - public static final int REQUEST_UPDATE_CURSOR_ANCHOR_INFO_MONITOR = 1 << 1; + public static final int CURSOR_UPDATE_MONITOR = 1 << 1; /** * Called by the input method to ask the editor for calling back * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} to * notify cursor/anchor locations. * - * @param cursorUpdateMode {@link #REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE} and/or - * {@link #REQUEST_UPDATE_CURSOR_ANCHOR_INFO_MONITOR} + * @param cursorUpdateMode {@link #CURSOR_UPDATE_IMMEDIATE} and/or + * {@link #CURSOR_UPDATE_MONITOR}. Pass {@code 0} to disable the effect of + * {@link #CURSOR_UPDATE_MONITOR}. * @return {@code true} if the request is scheduled. {@code false} to indicate that when the * application will not call * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)}. */ + public boolean requestCursorUpdates(int cursorUpdateMode); + + /** + * @removed + */ + public static final int REQUEST_UPDATE_CURSOR_UPDATE_IMMEDIATE = 1 << 0; + + /** + * @removed + */ + public static final int REQUEST_UPDATE_CURSOR_ANCHOR_INFO_MONITOR = 1 << 1; + + /** + * @removed + */ public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode); } diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java index d95df25..87853de 100644 --- a/core/java/android/view/inputmethod/InputConnectionWrapper.java +++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java @@ -126,7 +126,14 @@ public class InputConnectionWrapper implements InputConnection { return mTarget.performPrivateCommand(action, data); } - public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) { - return mTarget.requestUpdateCursorAnchorInfo(cursorUpdateMode); + public boolean requestCursorUpdates(int cursorUpdateMode) { + return mTarget.requestCursorUpdates(cursorUpdateMode); } - } + + /** + * @removed + */ + public final boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) { + return mTarget.requestCursorUpdates(cursorUpdateMode); + } +} diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 0a472c7..b56378f 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1526,7 +1526,7 @@ public final class InputMethodManager { * Return true if the current input method wants to watch the location * of the input editor's cursor in its window. * - * @deprecated Use {@link InputConnection#requestUpdateCursorAnchorInfo(int)} instead. + * @deprecated Use {@link InputConnection#requestCursorUpdates(int)} instead. */ @Deprecated public boolean isWatchingCursor(View view) { @@ -1542,9 +1542,9 @@ public final class InputMethodManager { public boolean isCursorAnchorInfoEnabled() { synchronized (mH) { final boolean isImmediate = (mRequestUpdateCursorAnchorInfoMonitorMode & - InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE) != 0; + InputConnection.CURSOR_UPDATE_IMMEDIATE) != 0; final boolean isMonitoring = (mRequestUpdateCursorAnchorInfoMonitorMode & - InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_MONITOR) != 0; + InputConnection.CURSOR_UPDATE_MONITOR) != 0; return isImmediate || isMonitoring; } } @@ -1608,7 +1608,7 @@ public final class InputMethodManager { // If immediate bit is set, we will call updateCursorAnchorInfo() even when the data has // not been changed from the previous call. final boolean isImmediate = (mRequestUpdateCursorAnchorInfoMonitorMode & - InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE) != 0; + InputConnection.CURSOR_UPDATE_IMMEDIATE) != 0; if (!isImmediate && Objects.equals(mCursorAnchorInfo, cursorAnchorInfo)) { // TODO: Consider always emitting this message once we have addressed redundant // calls of this method from android.widget.Editor. @@ -1624,7 +1624,7 @@ public final class InputMethodManager { mCursorAnchorInfo = cursorAnchorInfo; // Clear immediate bit (if any). mRequestUpdateCursorAnchorInfoMonitorMode &= - ~InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE; + ~InputConnection.CURSOR_UPDATE_IMMEDIATE; } catch (RemoteException e) { Log.w(TAG, "IME died: " + mCurId, e); } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index eb93745..eef8554 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -5717,8 +5717,16 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } @Override + public boolean requestCursorUpdates(int cursorUpdateMode) { + return getTarget().requestCursorUpdates(cursorUpdateMode); + } + + /** + * @removed + */ + @Override public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) { - return getTarget().requestUpdateCursorAnchorInfo(cursorUpdateMode); + return getTarget().requestCursorUpdates(cursorUpdateMode); } } diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index 1da22ca..b9f891c 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -297,10 +297,10 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { public boolean performItemClick(View view, int position, long id) { if (mOnItemClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); + mOnItemClickListener.onItemClick(this, view, position, id); if (view != null) { view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); } - mOnItemClickListener.onItemClick(this, view, position, id); return true; } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 46b225d..22138d0 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -3089,13 +3089,12 @@ public class Editor { final boolean isLeadingEdgeTopVisible = isPositionVisible(leadingEdgeX, top); final boolean isTrailingEdgeBottomVisible = isPositionVisible(trailingEdgeX, bottom); - final int characterRectFlags; - if (isLeadingEdgeTopVisible && isTrailingEdgeBottomVisible) { - characterRectFlags = CursorAnchorInfo.CHARACTER_RECT_TYPE_FULLY_VISIBLE; - } else if (isLeadingEdgeTopVisible || isTrailingEdgeBottomVisible) { - characterRectFlags = CursorAnchorInfo.CHARACTER_RECT_TYPE_PARTIALLY_VISIBLE; - } else { - characterRectFlags = CursorAnchorInfo.CHARACTER_RECT_TYPE_INVISIBLE; + int characterRectFlags = 0; + if (isLeadingEdgeTopVisible || isTrailingEdgeBottomVisible) { + characterRectFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION; + } + if (!isLeadingEdgeTopVisible || !isTrailingEdgeBottomVisible) { + characterRectFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION; } // Here offset is the index in Java chars. // TODO: We must have a well-defined specification. For example, how @@ -3117,11 +3116,19 @@ public class Editor { + viewportToContentVerticalOffset; final float insertionMarkerBottom = layout.getLineBottom(line) + viewportToContentVerticalOffset; - // Take TextView's padding and scroll into account. - final boolean isClipped = !isPositionVisible(insertionMarkerX, insertionMarkerTop) - || !isPositionVisible(insertionMarkerX, insertionMarkerBottom); + final boolean isTopVisible = + isPositionVisible(insertionMarkerX, insertionMarkerTop); + final boolean isBottomVisible = + isPositionVisible(insertionMarkerX, insertionMarkerBottom); + int insertionMarkerFlags = 0; + if (isTopVisible || isBottomVisible) { + insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION; + } + if (!isTopVisible || !isBottomVisible) { + insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION; + } builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop, - insertionMarkerBaseline, insertionMarkerBottom, isClipped); + insertionMarkerBaseline, insertionMarkerBottom, insertionMarkerFlags); } imm.updateCursorAnchorInfo(mTextView, builder.build()); diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 05ff151..69d5f40 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -864,7 +864,7 @@ public class RemoteViews implements Parcelable, Filter { if (alpha != -1) { targetDrawable.setAlpha(alpha); } - if (colorFilter != -1 && filterMode != null) { + if (filterMode != null) { targetDrawable.setColorFilter(colorFilter, filterMode); } if (level != -1) { @@ -2189,8 +2189,8 @@ public class RemoteViews implements Parcelable, Filter { * @param alpha Specify an alpha value for the drawable, or -1 to leave * unchanged. * @param colorFilter Specify a color for a - * {@link android.graphics.ColorFilter} for this drawable, or -1 - * to leave unchanged. + * {@link android.graphics.ColorFilter} for this drawable. This will be ignored if + * {@code mode} is {@code null}. * @param mode Specify a PorterDuff mode for this drawable, or null to leave * unchanged. * @param level Specify the level for the drawable, or -1 to leave diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 9b3a1e0..3e1b674 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -1861,6 +1861,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return getCompoundPaddingTop(); } + if (mLayout == null) { + assumeLayout(); + } + if (mLayout.getLineCount() <= mMaximum) { return getCompoundPaddingTop(); } @@ -1894,6 +1898,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return getCompoundPaddingBottom(); } + if (mLayout == null) { + assumeLayout(); + } + if (mLayout.getLineCount() <= mMaximum) { return getCompoundPaddingBottom(); } diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java index ece8aa4..3ba03b8 100644 --- a/core/java/android/widget/Toolbar.java +++ b/core/java/android/widget/Toolbar.java @@ -248,11 +248,12 @@ public class Toolbar extends ViewGroup { final Drawable navIcon = a.getDrawable(R.styleable.Toolbar_navigationIcon); if (navIcon != null) { setNavigationIcon(navIcon); - final CharSequence navDesc = a.getText( - R.styleable.Toolbar_navigationContentDescription); - if (!TextUtils.isEmpty(navDesc)) { - setNavigationContentDescription(navDesc); - } + } + + final CharSequence navDesc = a.getText( + R.styleable.Toolbar_navigationContentDescription); + if (!TextUtils.isEmpty(navDesc)) { + setNavigationContentDescription(navDesc); } a.recycle(); } diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java index cb0c3d0..572cca2 100644 --- a/core/java/android/widget/VideoView.java +++ b/core/java/android/widget/VideoView.java @@ -253,6 +253,10 @@ public class VideoView extends SurfaceView * * @param uri the URI of the video. * @param headers the headers for the URI request. + * Note that the cross domain redirection is allowed by default, but that can be + * changed with key/value pairs through the headers parameter with + * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value + * to disallow or allow cross domain redirection. */ public void setVideoURI(Uri uri, Map<String, String> headers) { mUri = uri; diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 61b4567..b6e7353 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -23,7 +23,6 @@ import android.app.usage.UsageStatsManager; import android.os.AsyncTask; import android.provider.Settings; import android.text.TextUtils; -import android.util.ArrayMap; import android.util.Slog; import android.widget.AbsListView; import android.widget.GridView; @@ -73,6 +72,7 @@ import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -100,7 +100,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic private boolean mResolvingHome = false; private UsageStatsManager mUsm; - private ArrayMap<String, UsageStats> mStats; + private Map<String, UsageStats> mStats; private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14; private boolean mRegistered; diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java index abe8a9f..6f1c7ec 100644 --- a/core/java/com/android/internal/app/ToolbarActionBar.java +++ b/core/java/com/android/internal/app/ToolbarActionBar.java @@ -22,6 +22,7 @@ import android.app.ActionBar; import android.content.Context; import android.content.res.Configuration; import android.graphics.drawable.Drawable; +import android.text.TextUtils; import android.view.ActionMode; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -32,6 +33,7 @@ import android.view.Window; import android.view.WindowCallbackWrapper; import android.widget.SpinnerAdapter; import android.widget.Toolbar; +import com.android.internal.R; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.widget.DecorToolbar; import com.android.internal.widget.ToolbarWidgetWrapper; @@ -44,6 +46,8 @@ public class ToolbarActionBar extends ActionBar { private boolean mToolbarMenuPrepared; private Window.Callback mWindowCallback; + private CharSequence mHomeDescription; + private boolean mLastMenuVisibility; private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners = new ArrayList<OnMenuVisibilityListener>(); @@ -70,6 +74,8 @@ public class ToolbarActionBar extends ActionBar { mDecorToolbar.setWindowCallback(mWindowCallback); toolbar.setOnMenuItemClickListener(mMenuClicker); mDecorToolbar.setWindowTitle(title); + mHomeDescription = mToolbar.getNavigationContentDescription(); + updateNavDescription(); } public Window.Callback getWrappedWindowCallback() { @@ -161,6 +167,7 @@ public class ToolbarActionBar extends ActionBar { @Override public void setHomeActionContentDescription(CharSequence description) { mToolbar.setNavigationContentDescription(description); + mHomeDescription = description; } @Override @@ -171,6 +178,7 @@ public class ToolbarActionBar extends ActionBar { @Override public void setHomeActionContentDescription(int resId) { mToolbar.setNavigationContentDescription(resId); + mHomeDescription = mToolbar.getNavigationContentDescription(); } @Override @@ -247,8 +255,22 @@ public class ToolbarActionBar extends ActionBar { @Override public void setDisplayOptions(@DisplayOptions int options, @DisplayOptions int mask) { - mDecorToolbar.setDisplayOptions((options & mask) | - mDecorToolbar.getDisplayOptions() & ~mask); + final int currentOptions = mDecorToolbar.getDisplayOptions(); + final int changed = (options ^ currentOptions) & mask; + mDecorToolbar.setDisplayOptions(options & mask | currentOptions & ~mask); + if ((changed & ActionBar.DISPLAY_HOME_AS_UP) != 0) { + updateNavDescription(); + } + } + + private void updateNavDescription() { + if ((mDecorToolbar.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) { + if (TextUtils.isEmpty(mHomeDescription)) { + mToolbar.setNavigationContentDescription(R.string.action_bar_up_description); + } else { + mToolbar.setNavigationContentDescription(mHomeDescription); + } + } } @Override diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index b1f5d90..e19b2b6 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -437,7 +437,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub { return; } args.callback.setRequestUpdateCursorAnchorInfoResult( - ic.requestUpdateCursorAnchorInfo(msg.arg1), args.seq); + ic.requestCursorUpdates(msg.arg1), args.seq); } catch (RemoteException e) { Log.w(TAG, "Got RemoteException calling requestCursorAnchorInfo", e); } diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index a8526c8..0c65ad1 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -428,7 +428,7 @@ public class InputConnectionWrapper implements InputConnection { } } - public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) { + public boolean requestCursorUpdates(int cursorUpdateMode) { boolean result = false; try { InputContextCallback callback = InputContextCallback.getInstance(); @@ -445,4 +445,11 @@ public class InputConnectionWrapper implements InputConnection { } return result; } + + /** + * @removed + */ + public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) { + return requestCursorUpdates(cursorUpdateMode); + } } diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index ba236f3..f211ff2 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -188,13 +188,13 @@ public class EditableInputConnection extends BaseInputConnection { } @Override - public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) { + public boolean requestCursorUpdates(int cursorUpdateMode) { if (DEBUG) Log.v(TAG, "requestUpdateCursorAnchorInfo " + cursorUpdateMode); // It is possible that any other bit is used as a valid flag in a future release. // We should reject the entire request in such a case. - final int KNOWN_FLAGS_MASK = InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE | - InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_MONITOR; + final int KNOWN_FLAGS_MASK = InputConnection.CURSOR_UPDATE_IMMEDIATE | + InputConnection.CURSOR_UPDATE_MONITOR; final int unknownFlags = cursorUpdateMode & ~KNOWN_FLAGS_MASK; if (unknownFlags != 0) { if (DEBUG) { @@ -212,7 +212,7 @@ public class EditableInputConnection extends BaseInputConnection { return false; } mIMM.setUpdateCursorAnchorInfoMode(cursorUpdateMode); - if ((cursorUpdateMode & InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE) != 0) { + if ((cursorUpdateMode & InputConnection.CURSOR_UPDATE_IMMEDIATE) != 0) { if (mTextView == null) { // In this case, FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE is silently ignored. // TODO: Return some notification code for the input method that indicates diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 71cf391..2114ff6 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -862,14 +862,23 @@ public class LockPatternUtils { * @return stored password quality */ public int getKeyguardStoredPasswordQuality() { - int quality = - (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); + return getKeyguardStoredPasswordQuality(getCurrentOrCallingUserId()); + } + + /** + * Retrieves the quality mode for {@param userHandle}. + * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} + * + * @return stored password quality + */ + public int getKeyguardStoredPasswordQuality(int userHandle) { + int quality = (int) getLong(PASSWORD_TYPE_KEY, + DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle); // If the user has chosen to use weak biometric sensor, then return the backup locking // method and treat biometric as a special case. if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) { - quality = - (int) getLong(PASSWORD_TYPE_ALTERNATE_KEY, - DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); + quality = (int) getLong(PASSWORD_TYPE_ALTERNATE_KEY, + DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle); } return quality; } |
